Zheng Junyi
4/4/2019
前面有一篇文章介绍了什么是 Rust 的生命周期?这里补充一些关于 Rust 的生命周期不容易被注意到的细节。
因为我水平有限,有的概念可能不是很全面,甚至可能有偏差,还请多多指点。
Rust 的看家本领就是静态检测,所有实例的生命周期都是在编译时就被确定好的。
所以不要问:“为什么生命周期不能根据实际情况自动确定?”因为真的做不到!
除了绑定所有者和数据外,let 还可以绑定所有者和数据之间的所有权。绑定了所有权,也就绑定了生命周期。
这是理所当然的事实,但是经常被人忽略。
与 let 相似,函数中的形式参数与实际参数的生命周期是相互绑定的。同样的,函数返回值和函数实体的拥有者的生命周期也是相互绑定的,因为返回值的生命周期与函数实体相互绑定,而函数实体与拥有者相互绑定。
举个例子:
x 与a 相互绑定,c 与 sum() 与 a + b 相互绑定。
结构体中也是类似的:
x 与 y 相互绑定,结构体 Foo 与 f 相互绑定。
这同样也是理所当然但容易被人忽视的事实,这里提出来也是为了给下一条做铺垫。
准确的说应该是生命周期标记可以通过影响函数 (结构体) 内部来对函数实体的拥有者和传入的实际参数产生影响。具体原因就是上面提到的形式参数与实际参数的生命周期相互绑定,函数返回值和函数实体的拥有者的生命周期相互绑定。
举个例子:
生命周期参数将 x 的生命周期与 results 相互绑定,而 x 与 a 相互绑定,results 与 c 相互绑定,所以 c 的生命周期与 a 是相互绑定的!!(就像三国杀中的「铁索连环」一样
根据关系我们可以很容易发现,a 必须活得比 c 长,不然就会报错。
从整体来考虑生命周期,显然更容易理解什么时候应该使用生命周期标注,什么时候不应该使用。
生命周期标注只是注释出:「谁应该比谁活得更长 (或者相等)」,如果实际情况与标注不相符,编译器会报错。
与直觉有所不同。显式生命周期标注是泛型参数一种,但是有协变的概念。
fn sum(x: u32, y: u32) -> u32 {
x + y
}
fn main(){
let a = 1;
let b = 2;
let c = sum(a, b);
}
struct Foo {
x: i32,
}
fn main() {
let y = 5;
let f = Foo { x: y };
}
fn foo<'a>(x: &'a str, y: &str) -> Vec<&'a str> {
let mut results = Vec::new();
if x.contains(y) {
results.push(x);
}
results
}
fn main() {
let a = "I'm foo";
let b = "foo";
let c = foo(a, b);
}