iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >rust生命周期源码分析
  • 721
分享到

rust生命周期源码分析

2023-07-05 13:07:00 721人浏览 八月长安
摘要

本文小编为大家详细介绍“rust生命周期源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“rust生命周期源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。rust生命周期生命周期是rust中用来规定引

本文小编为大家详细介绍“rust生命周期源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“rust生命周期源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

    rust生命周期

    生命周期是rust中用来规定引用的有效作用域。在大多数时候,无需手动声明,因为编译器能够自动推导。当编译器无法自动推导出生命周期的时候,就需要我们手动标明生命周期。生命周期主要是为了避免悬垂引用

    借用检查

    rust的编译器会使用借用检查器来检查我们程序的借用正确性。例如:

    #![allow(unused)]fn main() {{    let r;    {        let x = 5;        r = &x;    }    println!("r: {}", r);}}

    在编译期,Rust 会比较两个变量的生命周期,结果发现 r 明明拥有生命周期 'a,但是却引用了一个小得多的生命周期 'b,在这种情况下,编译器会认为我们的程序存在风险,因此拒绝运行。

    函数中的生命周期

    #![allow(unused)]fn main() {fn longest(x: &str, y: &str) -> &str {    if x.len() > y.len() {        x    } else {        y    }}}

    执行这段代码,rust编译器会报错,它给出的help信息如下:

    help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`

    意思是函数返回类型是一个借用值,但是无法从函数的签名中得知返回值是从x还是y借用的。并且给出了相应的修复代码。

    4 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str  |           ++++     ++          ++          ++

    按照这个提示,我们更改函数声明。就会发现可以顺利通过编译。因此,像这样的函数,我们无法判断它是返回x还是y,那么只好手动进行生命周期声明。上面的提示就是手动声明声明周期的语法。

    手动声明生命周期

    需要注意的是,标记的生命周期只是为了取悦编译器,让编译器不要难为我们,它不会改变任何引用的实际作用域

    生命周期的语法是以&rsquo;开头,名称往往是一个单独的小写字母。大多数人用&rsquo;a来作为生命周期的名称。如果是引用类型的参数,生命周期会位于&之后,并用空格来将生命周期和参数分隔开。函数签名中的生命周期标注和泛型一样,需要在提前声明生命周期。例如我们刚才修改过的函数签名

    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str

    该函数签名表明对于某些生命周期 'a,函数的两个参数都至少跟 'a 活得一样久,同时函数的返回引用也至少跟 'a 活得一样久。实际上,这意味着返回值的生命周期与参数生命周期中的较小值一致:虽然两个参数的生命周期都是标注了 'a,但是实际上这两个参数的真实生命周期可能是不一样的(生命周期 'a 不代表生命周期等于 'a,而是大于等于 'a)。例如:

    fn main() {    let string1 = String::from("long string is long");    {        let string2 = String::from("xyz");        let result = longest(string1.as_str(), string2.as_str());        println!("The longest string is {}", result);    }}

    result 的生命周期等于参数中生命周期最小的,因此要等于 string2 的生命周期,也就是说,result 要活得和 string2 一样久。如过我们将上面的代码改变为如下所示。

    fn main() {    let string1 = String::from("long string is long");    let result;    {        let string2 = String::from("xyz");        result = longest(string1.as_str(), string2.as_str());    }    println!("The longest string is {}", result);}

    那么将会导致错误,因为编译器知道string2活不到最后一行打印。而string1可以活到打印,但是编译器并不知道longest返回的是谁。
    函数的返回值如果是一个引用类型,那么它的生命周期只会来源于:

    • 函数参数的生命周期

    • 函数体中某个新建引用的生命周期

    若是后者情况,就是典型的悬垂引用场景:

    #![allow(unused)]fn main() {fn longest<'a>(x: &str, y: &str) -> &'a str {    let result = String::from("really long string");    result.as_str()}}

    上面的函数的返回值就和参数 x,y 没有任何关系,而是引用了函数体内创建的字符串,而函数结束的时候会自动释放result的内存,从而导致悬垂指针。这种情况,最好的办法就是返回内部字符串的所有权,然后把字符串的所有权转移给调用者:

    fn longest<'a>(_x: &str, _y: &str) -> String {    String::from("really long string")}fn main() {   let s = longest("not", "important");}

    结构体中的生命周期

    在结构体中使用引用,只要为结构体中的每一个引用标注上生命周期即可。

    struct ImportantExcerpt<'a> {    part: &'a str,}fn main() {    let novel = String::from("Call me Ishmael. Some years aGo...");    let first_sentence = novel.split('.').next().expect("Could not find a '.'");    let i = ImportantExcerpt {        part: first_sentence,    };}

    part引用的first_sentence来自于novel,它的生命周期是main函数,因此这段代码可以正常工作。
    ImportantExcerpt 结构体中有一个引用类型的字段 part,因此需要为它标注上生命周期。结构体的生命周期标注语法跟泛型参数语法很像,需要对生命周期参数进行声明 <'a>。该生命周期标注说明,结构体 ImportantExcerpt 所引用的字符串 str 必须比该结构体活得更久。

    生命周期消除

    编译器为了简化用户的使用,运用了生命周期消除大法。例如:

    fn first_word(s: &str) -> &str {    let bytes = s.as_bytes();    for (i, &item) in bytes.iter().enumerate() {        if item == b' ' {            return &s[0..i];        }    }    &s[..]}

    对于 first_Word 函数,它的返回值是一个引用类型,那么该引用只有两种情况:

    • 从参数获取

    • 从函数体内部新创建的变量获取

    如果是后者,就会出现悬垂引用,最终被编译器拒绝,因此只剩一种情况:返回值的引用是获取自参数,这就意味着参数和返回值的生命周期是一样的。道理很简单,我们能看出来,编译器自然也能看出来,因此,就算我们不标注生命周期,也不会产生歧义。

    只不过,消除规则不是万能的,若编译器不能确定某件事是正确时,会直接判为不正确,那么你还是需要手动标注生命周期
    函数或者方法中,参数的生命周期被称为 输入生命周期,返回值的生命周期被称为 输出生命周期

    三条消除原则

    每一个引用参数都会获得独自的生命周期

    例如一个引用参数的函数就有一个生命周期标注: fn foo<'a>(x: &'a i32),两个引用参数的有两个生命周期标注:fn foo<'a, 'b>(x: &'a i32, y: &'b i32), 依此类推。

    若只有一个输入生命周期(函数参数中只有一个引用类型),那么该生命周期会被赋给所有的输出生命周期,也就是所有返回值的生命周期都等于该输入生命周期

    例如函数 fn foo(x: &i32) -> &i32,x 参数的生命周期会被自动赋给返回值 &i32,因此该函数等同于 fn foo<'a>(x: &'a i32) -> &'a i32

    若存在多个输入生命周期,且其中一个是 &self 或 &mut self,则 &self 的生命周期被赋给所有的输出生命周期。

    拥有 &self 形式的参数,说明该函数是一个 方法,该规则让方法的使用便利度大幅提升。

    让我们假装自己是编译器,然后看下以下的函数该如何应用这些规则:

    例子1

    fn first_word(s: &str) -> &str // 实际项目中的手写代码

    首先,我们手写的代码如上所示时,编译器会先应用第一条规则,为每个参数标注一个生命周期:

    fn first_word<'a>(s: &'a str) -> &str  // 编译器自动为参数添加生命周期

    此时,第二条规则就可以进行应用,因为函数只有一个输入生命周期,因此该生命周期会被赋予所有的输出生命周期:

    fn first_word<'a>(s: &'a str) -> &'a str  // 编译器自动为返回值添加生命周期

    此时,编译器为函数签名中的所有引用都自动添加了具体的生命周期,因此编译通过,且用户无需手动去标注生命周期,只要按照 fn first_word(s: &str) -> &str的形式写代码即可。

    例子2

    fn longest(x: &str, y: &str) -> &str // 实际项目中的手写代码

    首先,编译器会应用第一条规则,为每个参数都标注生命周期:

    fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str

    但是此时,第二条规则却无法被使用,因为输入生命周期有两个,第三条规则也不符合,因为它是函数,不是方法,因此没有 &self 参数。在套用所有规则后,编译器依然无法为返回值标注合适的生命周期,因此,编译器就会报错,提示我们需要手动标注生命周期。

    例子3

    impl<'a> ImportantExcerpt<'a> {    fn announce_and_return_part(&self, announcement: &str) -> &str {        println!("Attention please: {}", announcement);        self.part    }}

    首先,编译器应用第一规则,给予每个输入参数一个生命周期。

    impl<'a> ImportantExcerpt<'a> {    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &str {        println!("Attention please: {}", announcement);        self.part    }}

    需要注意的是,编译器不知道 announcement 的生命周期到底多长,因此它无法简单的给予它生命周期 'a,而是重新声明了一个全新的生命周期 'b。接着,编译器应用第三规则,将 &self 的生命周期赋给返回值 &str

    impl<'a> ImportantExcerpt<'a> {    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'a str {        println!("Attention please: {}", announcement);        self.part    }}

    尽管我们没有给方法标注生命周期,但是在第一和第三规则的配合下,编译器依然完美的为我们亮起了绿灯。

    生命周期约束

    我们来看下面这个例子。将返回值的生命周期声明为&rsquo;b,但是实际返回的是生命周期为&rsquo;a的self.part。

    impl<'a: 'b, 'b> ImportantExcerpt<'a> {    fn announce_and_return_part(&'a self, announcement: &'b str) -> &'b str {        println!("Attention please: {}", announcement);        self.part    }}
    • 'a: 'b,是生命周期约束语法,跟泛型约束非常相似,用于说明 'a 必须比 'b 活得久

    • 可以把 'a 和 'b 都在同一个地方声明(如上),或者分开声明但通过 where 'a: 'b 约束生命周期关系,如下:

    impl<'a> ImportantExcerpt<'a> {    fn announce_and_return_part<'b>(&'a self, announcement: &'b str) -> &'b str    where        'a: 'b,    {        println!("Attention please: {}", announcement);        self.part    }}

    加上这个约束,告诉编译器&rsquo;a活的比&rsquo;b更久,引用&rsquo;a不会产生悬垂指针(无效引用)。

    静态生命周期

    rust中有一个非常特殊的生命周期,那就是&rsquo;static,拥有该生命周期的引用可以活的和整个程序一样久。实际上字符串字面值就拥有&rsquo;static生命周期,它被硬编码进rust的二进制文件中。'static生命周期非常强大,随意使用它相当于放弃了生命周期检查。遇到因为生命周期导致的编译不通过问题,首先想的应该是:是否是我们试图创建一个悬垂引用,或者是试图匹配不一致的生命周期,而不是简单粗暴的用 'static 来解决问题。除非实在遇到解决不了的生命周期标注问题,可以尝试&rsquo;static生命周期。例如:

    fn t() -> &'static str{    "qwert"}fn t() -> &'static str{    "qwert"}

    注意,使用&rsquo;static生命周期的时候,不需要提前声明。

    一个复杂例子: 泛型,特征约束以及生命周期

    use std::fmt::Display;fn longest_with_an_announcement<'a, T>(    x: &'a str,    y: &'a str,    ann: T,) -> &'a strwhere    T: Display,{    println!("Announcement! {}", ann);    if x.len() > y.len() {        x    } else {        y    }}

    例子中,包含了生命周期&rsquo;a,泛型T以及对T的约束Display(因为我们需要打印ann)。

    读到这里,这篇“rust生命周期源码分析”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网精选频道。

    --结束END--

    本文标题: rust生命周期源码分析

    本文链接: https://www.lsjlt.com/news/351946.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • rust生命周期源码分析
      本文小编为大家详细介绍“rust生命周期源码分析”,内容详细,步骤清晰,细节处理妥当,希望这篇“rust生命周期源码分析”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。rust生命周期生命周期是rust中用来规定引...
      99+
      2023-07-05
    • Spring Bean生命周期源码分析
      这篇“Spring Bean生命周期源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring ...
      99+
      2023-07-05
    • Rust语言生命周期的示例分析
      本篇文章为大家展示了Rust语言生命周期的示例分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Rust 官方博客发布了 2020 年度的 Rust 调查报告。此次...
      99+
      2024-04-02
    • Python对象的生命周期源码分析
      本篇内容介绍了“Python对象的生命周期源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!思考:当我们输入这个语句的时候,Python...
      99+
      2023-06-30
    • 解析Rust struct 中的生命周期
      最近在用rust 写一个redis的数据校验工具。redis-rs中具备 redis::ConnectionLike trait,借助它可以较好的来抽象校验过程。在开发中,不免要定义...
      99+
      2024-04-02
    • rust生命周期详解
      目录rust生命周期借用检查函数中的生命周期手动声明生命周期结构体中的生命周期生命周期消除三条消除原则生命周期约束静态生命周期rust生命周期 生命周期是rust中用来规定引用的有效...
      99+
      2023-03-19
      rust生命周期
    • react中context传值和生命周期源码分析
      本篇内容主要讲解“react中context传值和生命周期源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“react中context传值和生命周期源码分析”吧!假设:项目中存在复杂组件树:...
      99+
      2023-07-05
    • Vue八大生命周期钩子函数源码分析
      本篇内容主要讲解“Vue八大生命周期钩子函数源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue八大生命周期钩子函数源码分析”吧!一.速识概念:我们把一个对象从生成(new)到被销毁(d...
      99+
      2023-07-05
    • Spring源码解析之Bean的生命周期
      一、Bean的实例化概述 前一篇分析了BeanDefinition的封装过程,最终将beanName与BeanDefinition以一对一映射关系放到beanDefinitionMa...
      99+
      2024-04-02
    • Java之Spring Bean作用域和生命周期源码分析
      这篇文章主要讲解了“Java之Spring Bean作用域和生命周期源码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java之Spring Bean作用域和生命周期...
      99+
      2023-07-05
    • 微信小程序开发中源码分析生命周期
      这篇文章主要介绍了微信小程序开发中源码分析生命周期的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇微信小程序开发中源码分析生命周期文章都会有所收获,下面我们一起来看看吧。生命周期的概念在讲微信小程序生命周期之前,...
      99+
      2023-07-05
    • 深入了解Rust的生命周期
      Rust生命周期简介 Rust 中的每一个引用都有其 生命周期(lifetime),也就是引用保持有效的作用域。 生命周期的主要目标是避免悬垂引用,它会导致程序引用了非预期引用的数据...
      99+
      2022-12-14
      Rust 生命周期机制 Rust 生命周期
    • Rust的生命周期是怎样的
      这篇文章主要介绍“Rust的生命周期是怎样的”,在日常操作中,相信很多人在Rust的生命周期是怎样的问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Rust的生命周期是怎样的”的疑惑有所帮助!接下来,请跟着小编...
      99+
      2023-06-27
    • SpringBoot源码之Bean的生命周期
      入口方法为SpringApplication#run() 1.SpringApplication#run() public ConfigurableApplicationCont...
      99+
      2023-05-15
      SpringBoot之Bean的生命周期 bean生命周期 SpringBean生命周期
    • Vue生命周期实例分析
      这篇文章主要介绍“Vue生命周期实例分析”,在日常操作中,相信很多人在Vue生命周期实例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue生命周期实例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
      99+
      2023-07-02
    • vue生命周期的示例分析
      这篇文章主要介绍了vue生命周期的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。vue生命周期图解感谢你能够认真阅读完这篇文章,希望小编分享的“vue生命周期的示例分...
      99+
      2023-06-14
    • Vue2.0生命周期的示例分析
      这篇文章主要为大家展示了“Vue2.0生命周期的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue2.0生命周期的示例分析”这篇文章吧。网上已经有很多...
      99+
      2024-04-02
    • React的生命周期实例分析
      这篇文章主要讲解了“React的生命周期实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“React的生命周期实例分析”吧!一、React生命周期React 生命周期分为三种状态 初始化...
      99+
      2023-07-02
    • Laravel的生命周期实例分析
      本篇内容主要讲解“Laravel的生命周期实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Laravel的生命周期实例分析”吧!Laravel的生命周期 A世间万物皆有生命周期,当我们使用...
      99+
      2023-06-30
    • Rust指南之生命周期机制详解
      目录前言1、所有权中的垂悬引用解析2、结构体中使用String 而不用&str 的原因3、生命周期注释4、结构体中使用字符串切片引用5、静态生命周期6、泛型、特性与生命周期综...
      99+
      2022-11-13
      Rust生命周期 Rust生命周期机制
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作