Rust 生命周期(有效期)通俗解释
有效期标注与变量依赖
我不知道为什么大家写得那么啰嗦。花了十分钟搞懂之后,恍然大悟,其实就是八个字:必须确保依赖有效。下面均举某 Rust Course 书的例子。
定义,变量的有效期为:开始于其自身的原作用域之开始,结束于其依赖项的作用域结束。
例(0)
1/*0*/{
2/*1*/ let r;
3/*2*/
4/*3*/ {
5/*4*/ let x = 5;
6/*5*/ r = &x;
7/*6*/ }
8/*7*/
9/*8*/ println!("r: {}", r);
10/*9*/}
-
r 的(原本)作用域:1~9 行。
-
依赖的作用域:4~5 行。
因此,r 的真实有效期为 1~5 行。故第 8 行引用了无效的 r,因此编译报错。
(1)下面代码为何不对?
1{
2 let r; // ---------+-- 'a
3 // |
4 { // |
5 let x = 5; // -+-- 'b |
6 r = &x; // | |
7 } // -+ |
8 // |
9 println!("r: {}", r); // |
10} // ---------+
答:因为 r 的有效期是 'a
,r 对 x
的引用(r = &x;
)在下一行随着 x 的失效而失效。故不安全。
这个道理很显然:若 a 依赖 b,则 b 的有效期应当大于 a
函数参数与返回值间的依赖
(2)下面代码为何不对?
1fn main() {
2 let string1 = String::from("abcd");
3 let string2 = "xyz";
4
5 let result = longest(string1.as_str(), string2);
6 println!("The longest string is {}", result);
7}
8
9
10fn longest(x: &str, y: &str) -> &str {
11 if x.len() > y.len() {
12 x
13 } else {
14 y
15 }
16}
分析依赖:longest 的返回值(记作 a),a 依赖 x, y,而 x, y 的有效期未知,导致 r 的有效期未知,故不安全。
解决方法:标注返回值的生命周期 'a
,到 x, y 上:
1fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
2 if x.len() > y.len() {
3 x
4 } else {
5 y
6 }
7}
从而表明返回值 a 的依赖项 x, y 具有相同有效期(或者说,x, y 的有效期大于等于 a)。从而通过检查。
(3)下面代码为何不对?
1/*1*/fn main() {
2/*2*/ let string1 = String::from("long string is long");
3/*3*/ let result;
4/*4*/ {
5/*5*/ let string2 = String::from("xyz");
6/*6*/ result = longest(string1.as_str(), string2./**/as_str());
7/*7*/ }
8/*8*/ println!("The longest string is {}", result);
9/*9*/}
解:
依赖图如下:
注:A->B 表示 B 依赖于 A
可见 result 的实际有效期为整数区间 $[3,6]$。
而 print!
调用时,依赖的 result 已失效,故报错。
如何修改 longest
解决这个问题?注意到,罪魁祸首在于 string2,因此,除非改变函数对 string2 的依赖,否则无解。
结构体与其成员的依赖
结构由成员构成,则结构依赖于成员,则成员有效期必须大于结构有效期。
1struct ImportantExcerpt<'a> {
2 part: &'a str,
3}
这里很简单,不详述。
有效期自动推断
例题,分析代码:
1fn first_word(s: &str) -> &str {
2 let bytes = s.as_bytes();
3
4 for (i, &item) in bytes.iter().enumerate() {
5 if item == b' ' {
6 return &s[0..i];
7 }
8 }
9
10 &s[..]
11}
解:设返回值为 a,则 a 只依赖于 s,故 a 的有效期为 s 的有效期。
要是不嫌累,也可以看依赖图:
还有其他规则,此处略。
寿命表示法
例(注:'a: 'b
,表示有效期 a > b)
1struct ImportantExcerpt<'a> {
2 part: &'a str,
3}
4
5impl<'a: 'b, 'b> ImportantExcerpt<'a> {
6 fn foo(&'a self, name: &'b str) -> &'b str {
7 println!("Attention please: {}", name);
8 self.part
9 }
10}
这表明实现方法 foo,其参数 b 比 self 短寿。并返回比和 b 同样短寿的值。这对于依赖关系是合法的。
static
'static
类似 C++ 的 static
,表示静态有效期,即不依赖任何变量。
参考
本文没讲各个消除规则,推荐继续阅读: