1,725
社区成员




上一条:C++ 生命周期回顾 下一条:结构体成员的生命周期(lifetime)标记
(1)首先,Rust的编译器需要明确地知道一个借用对象是否还是有效的。例如返回一个新创建的对象肯定是有效的,不需要检查。
fn create_obj():Object{
Object{}
}
(2)但是,显然你不能返回一个局部对象的借用,因为局部对象在函数结束后超出作用域就被释放了:
fn get_obj():&Object{ // compile error
const obj = Object{};
&obj
}
(3)不过,如果这个借用本来就是从外部传入的,那当然可以返回,函数结束后这个对象还是有效的:
// I am borrowed from caller
// return borrow to the caller is safe
fn process_obj(obj:&Object):&Object{
&obj
}
(4)然而,如果你传入了两个对象的借用,内部做了条件返回。那么编译器没那么智能,它并不总是能推断出返回的是哪个对象的借用:
// compile error!
// where am I come from?
fn process_objs(x:&Obejct, y:&Object):&Object{
if(x.is_ok()){
&x
}else{
&y
}
}
(5)因此,Rust保留了内部的一种编译器内部的,本来是隐式添加记号,也就是生命周期(lifetime),通过显式添加生命周期标记,解决上述问题:
// I am come from 'a lifetime, NOT 'b
fn process_objs<'a,'b>(x: &'a Obejct, y:&'b Object):&'a Object{
&x
}
// I am come from 'a lifetime, x,y,and result are all 'a lifetime
fn process_objs<'a>(x: &'a Obejct, y:&'a Object):&'a Object{
if(x.is_ok()){
&x
}else{
&y
}
}
(6)事实上,当你没写lifetime标记时,每个对象也都是有对应的lifetime的,例如编译器为每个对象生成一个不同的lifetime
fn test<'a,'b>(x: &'a Obejct, y:&'b Object){
}
(7)因为默认生成的都是不同的,所以返回值如果不标记是谁,编译器就无法推断:
fn test<'a,'b,'c>(x: &'a Obejct, y:&'b Object):&'c Object{ // 'c is 'a or 'b ?
if(x.is_ok()){
&x
}else{
&y
}
}
(8)所以如果我们显式标记,并让两个变量用同一个,就能解决,这就是告诉编译器,`'c='a='b`:
fn test<'a>(x: &'a Obejct, y:&'a Object):&'a Object{ // 'c='a='b, they are all 'a
if(x.is_ok()){
&x
}else{
&y
}
}
(9)看到这里,你也应该知道了lifetime标记的名字是任意的,只是一个【形参】,代表的是这个借用对象的生命周期作用域的名字:
{
let obj; //---'a start here
{
let x = Obj{}; //---'b start here
obj = &x; //---'b finish here
}
println!("obj: {}", obj); //---'a finish here, 'b is out of scope, compile error!
}
// #[derive(Apparition)]
{
// 当然你可以用任意合法的符号替换'a和'b,它们只是个名字
let obj<'b>; //---'a start here
{
let x = Obj{}; //---'b start here
obj<'b> = &'b x; //---'b finish here
}
// obj借用是否有效,仅仅取决于它实际上它所借用的对象的生命周期作用域'b范围是否大于等于'a
// 一个'b作用域内的对象的借用,在'a内被调用,但是'b比'a小,调用的时候'b已经不存在了
// 因此编译器宣布:这是非法的。
println!("obj: {}", obj<'b>); //---'a finish here, 'b is out of scope, compile error!
}
不错