我正在阅读Rust书的生命周期章节,我看到了一个命名/显式生命周期的例子:
struct Foo<'a> {
x: &'a i32,
}
fn main() {
let x; // -+ x goes into scope
// |
{ // |
let y = &5; // ---+ y goes into scope
let f = Foo { x: y }; // ---+ f goes into scope
x = &f.x; // | | error here
} // ---+ f and y go out of scope
// |
println!("{}", x); // |
} // -+ x goes out of scope
我很清楚,编译器阻止的错误是分配给x的引用的use-after-free:内部作用域完成后,f和&f。X变得无效,并且不应该分配给X。
我的问题是,这个问题可以很容易地分析掉,而不使用显式的` a lifetime,例如,通过推断一个更广泛的范围的引用的非法赋值(x = &f.x;)。
在哪些情况下,实际上需要显式生命期来防止free后使用(或其他类?)错误?
让我们来看看下面的例子。
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
x
}
fn main() {
let x = 12;
let z: &u32 = {
let y = 42;
foo(&x, &y)
};
}
在这里,显式生命期很重要。这是因为foo的结果与它的第一个参数('a)具有相同的生命期,所以它可能比它的第二个参数更长寿。这是由foo签名中的生存期名称表示的。如果你在foo调用中切换参数,编译器会抱怨y的生存时间不够长:
error[E0597]: `y` does not live long enough
--> src/main.rs:10:5
|
9 | foo(&y, &x)
| - borrow occurs here
10 | };
| ^ `y` dropped here while still borrowed
11 | }
| - borrowed value needs to live until here
If a function receives two references as arguments and returns a reference, then the implementation of the function might sometimes return the first reference and sometimes the second one. It is impossible to predict which reference will be returned for a given call. In this case, it is impossible to infer a lifetime for the returned reference, since each argument reference may refer to a different variable binding with a different lifetime. Explicit lifetimes help to avoid or clarify such a situation.
同样地,如果一个结构体包含两个引用(作为两个成员字段),那么该结构体的成员函数可能有时返回第一个引用,有时返回第二个引用。同样,显式的生存期可以防止这种歧义。
在一些简单的情况下,存在生命期省略,编译器可以推断生命期。
注意,除了结构定义之外,这段代码中没有显式的生存期。编译器完全能够在main()中推断生存期。
然而,在类型定义中,显式生存期是不可避免的。例如,这里有一个歧义:
struct RefPair(&u32, &u32);
这是不同的人生还是相同的人生?从使用的角度来看,结构体RefPair<'a, 'b>(&'a u32, &'b u32)与结构体RefPair<'a>(&'a u32, &'a u32)非常不同。
现在,对于简单的情况,就像你提供的那样,理论上编译器可以像在其他地方一样省略生命期,但这样的情况非常有限,不值得在编译器中增加额外的复杂性,这种清晰度的增加至少是有问题的。