【Rust自学】10.6. 生命周期 Pt.2:生命周期的语法与例子

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=^・ω・^=)

10.6.1. 生命周期标注语法

  • 生命周期的标注并不会改变引用的生命周期长度。
  • 如果某个函数它制定了泛型生命周期参数,那么它就可以接收带有任何生命周期的引用。
  • 生命周期的标准主要是用于描述多个引用的生命周期之间的关系,但不影响生命周期。

生命周期的参数名称必须以'开头,通常是全小写且非常短的。很多开发者使用'a作为生命周期参数的名称。

生命周期的标注放在&符号后,在标注后边使用空格将标注和引用类型分开。

10.6.2. 生命周期标注例子

  • &i32:一个普通的引用
  • &'a i32:带有显式生命周期的引用,引用指向的类型就是i32
  • &'a mut i32:带有显式生命周期的可变引用

单个生命周期的标注本身没有意义,生命周期标注存在的意义是向Rust描述多个泛型生命周期之间的参数的关系。

以上一篇文章的代码为例:

rust 复制代码
fn main() {  
    let string1 = String::from("abcd");  
    let string2 = "xyz";  
  
    let result = longest(string1.as_str(), string2);  
    println!("The longest string is {result}");  
}  
  
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

longest中的形参xy以及返回值的生命周期都是'a,这就意味着xy和返回值必须拥有"相同的"生命周期。

通过刚才的代码例也看到了,在函数签名中使用生命周期标注需要把泛型生命周期参数生命在<>里。这个签名会告诉Rust有这么一个生命周期'a,而xy和返回值的存活时间必须不短于'a

因为生命周期的标准主要是用于描述多个引用的生命周期之间的关系,但不影响生命周期。 所以这么写并不会影响实参的生命周期,这样写只是为借用检查器指出了一些可用于检查非法调用的一些约束而已。所以longest函数并不需要知道xy具体的存活时长,只需要某个作用域可以被用来代替'a,同时满足函数的签名约束即可。

如果函数引用它外部的代码,或者说它被外部的代码引用的时候,想单靠Rust本身来确定参数和返回值的生命周期几乎就是不可能的了。这样函数所使用的生命周期可能在每次调用中都发生变化。正是因此才需要手动对生命周期进行标注

在代码例中,当我们把具体的引用传入longest函数的时候,被用来代替'a的生命周期的作用域是哪一块呢?就是x的作用域和y的作用域所重叠的部分,也就是两者中生命周期较短的那个的生命周期 。又因为返回值的生命周期也是'a,所以说返回的引用在x的作用域和y的作用域所重叠的部分都是有效的。

这就是为什么在前一篇文章和本文的前面对于"相同的"这个词都使用了引号标注,因为它并不是字面意义上的相同,而是指重叠的部分。

下面来看一下生命周期标注是如何对longest函数调用进行限制的。如果我们改一下上边的代码例,把string1的作用域改并把string2改为String类型会怎么样:

rust 复制代码
fn main() {  
    let string1 = String::from("abcd");  
    {  
        let string2 = String::from("xyz");  
        let result = longest(string1.as_str(), string2.as_str());  
        println!("The longest string is {result}");  
    }  
}  
  
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

这样string1的作用域是从第2行到第8行,string2的作用域是从第4行到第7行。函数会寻找重叠的部分(或者说较短的那个生命周期),也就是string2的作用域第4行到第7行,所以'a指代的作用域就是第4行到第7行。result在内部作用域,也就是花括号结束之前(第7行)一直有效,在'a的作用域内,所以代码仍然有效。

那如果我改变result的作用域呢:

rust 复制代码
fn main() {  
    let string1 = String::from("abcd");  
    let result;  
    {  
        let string2 = String::from("xyz");  
        result = longest(string1.as_str(), string2.as_str());  
    }  
    println!("The longest string is {result}");  
}  
  
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  
    if x.len() > y.len() {  
        x  
    } else {  
        y  
    }  
}

这个时候string1的作用域是从第2行到第9行,string2的作用域是从第5行到第7行,将这两者传入longest,函数会寻找重叠的部分(或者说较短的那个生命周期),也就是string2的作用域第5行到第7行,所以函数的泛型作用域参数'a就是第5行到第7行,那么返回值的作用域也该是第5行到第7行。但是用于接收返回值的result变量的作用域实际是第3行到第9行,超出了'a指代的作用域,所以程序会报错:

error[E0597]: `string2` does not live long enough
 --> src/main.rs:6:44
  |
5 |         let string2 = String::from("xyz");
  |             ------- binding `string2` declared here
6 |         result = longest(string1.as_str(), string2.as_str());
  |                                            ^^^^^^^ borrowed value does not live long enough
7 |     }
  |     - `string2` dropped here while still borrowed
8 |     println!("The longest string is {result}");
  |                                     -------- borrow later used here

编译器会提示string2的存活时间不够。为了保证第8行的打印的result有效,那么string2必须在外部作用域结束之前一直保持有效。因为函数的参数和返回值是用了相同的生命周期,所以Rust才会指出这个问题。

最后再重复一遍这篇文章最重要的知识点:生命周期'a的实际生命周期是取xy两个生命周期中较小的那个。

相关推荐
AI向前看2 分钟前
C语言的数据结构
开发语言·后端·golang
Rossy Yan4 分钟前
【C++数据结构——查找】二分查找(头歌实践教学平台习题)【合集】
开发语言·数据结构·c++·算法·查找·头歌实践教学平台·合集
架构文摘JGWZ10 分钟前
一键完成!!网页打包成桌面应用
开发语言·学习·开源软件·工具
m0_7482396310 分钟前
从零开始的vscode配置及安装rust教程
ide·vscode·rust
快乐非自愿10 分钟前
一文解秘Rust如何与Java互操作
java·开发语言·rust
SomeB1oody11 分钟前
【Rust自学】10.8. 生命周期 Pt.4:方法定义中的生命周期标注与静态生命周期
开发语言·后端·rust
许野平12 分钟前
Rust:运行调用 Lua 脚本
rust·lua·rlua
Source.Liu16 分钟前
【学Rust开发CAD】1 环境搭建
开发语言·rust
Source.Liu17 分钟前
【学Rust开发CAD】2 创建第一个工作空间、项目及库
rust·cad
文浩(楠搏万)27 分钟前
Java内存管理:不可达对象分析与内存泄漏优化技巧 Eclipse Memory Analyzer
java·开发语言·缓存·eclipse·内存泄漏·不可达对象·对象分析