喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=^・ω・^=)
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
中的形参x
和y
以及返回值的生命周期都是'a
,这就意味着x
、y
和返回值必须拥有"相同的"生命周期。
通过刚才的代码例也看到了,在函数签名中使用生命周期标注需要把泛型生命周期参数生命在<>
里。这个签名会告诉Rust有这么一个生命周期'a
,而x
、y
和返回值的存活时间必须不短于'a
。
因为生命周期的标准主要是用于描述多个引用的生命周期之间的关系,但不影响生命周期。 所以这么写并不会影响实参的生命周期,这样写只是为借用检查器指出了一些可用于检查非法调用的一些约束而已。所以longest
函数并不需要知道x
和y
具体的存活时长,只需要某个作用域可以被用来代替'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
的实际生命周期是取x
和y
两个生命周期中较小的那个。