Rust中生命周期是否会从代码块逃逸?两类案例矛盾解析
自定义结构体可变引用生命周期“逃逸”问题解析
问题场景
以下Rust代码无法编译,报错信息:cannot borrow 'a' as mutable more than once at a time。代码块内的可变引用a1本该在代码块结束时终止生命周期,却导致后续无法再次可变借用a:
struct container<'x> { value: &'x i32, } impl<'x> container<'x> { fn f(&'x mut self) {} } fn main() { let v = 1; let mut a = container{value: &v}; // 'a----------+ { // | let a1 = & mut a; //'b---------------------+ | a1.f(); // | | //end--------------------------------------+ | } // | //end--------------------------------------------+ let p = & mut a; // suppose to be 'c but 'b break out from block? }
作为对比,这段代码可以正常编译:
fn i32_f<'a>(k: &'a mut i32) {} fn main() { let mut v:i32 = 1; let mut i32_a = &mut v; { // | let i32_a1 = & mut i32_a; //'b-------------+ | i32_f(i32_a1); // | | //end--------------------------------------+ | } // | //end--------------------------------------------+ let i32_p = & mut i32_a; // works }
疑问:为何container类型的引用生命周期能从代码块“逃逸”,而i32类型的引用却不会?这似乎违背了生命周期的一致性规则。
核心原因
问题出在container的f方法签名上:fn f(&'x mut self) {},你把方法的self引用生命周期强制绑定到了结构体自身的生命周期'x。
具体拆解:
- 结构体
container<'x>的生命周期'x是从main中a被创建开始,一直到main函数结束(也就是注释里的'a)。 - 当调用
a1.f()时,a1的生命周期是代码块内的'b,但方法f要求传入的self引用生命周期必须是'x。Rust的借用检查器会强制把'b和'x对齐——也就是让'b的长度等于'x,相当于把a1的生命周期延长到了整个main函数。 - 这样一来,代码块结束后,
a1的可变引用依然被认为是“有效”的,后续再创建p这个可变引用时,就触发了Rust的核心规则:同一时间只能有一个可变引用。
而i32的例子中没有这个问题:
i32_f的签名是fn i32_f<'a>(k: &'a mut i32),这里的生命周期'a是独立的,只需要覆盖函数调用的时间段即可。代码块结束后,i32_a1的生命周期正常终止,所以后续可以再次借用i32_a。
修复方法
只需要修改container的f方法签名,不要把self的引用生命周期和结构体的'x绑定,使用独立的生命周期(或者利用Rust的生命周期省略规则):
impl<'x> container<'x> { // 使用独立生命周期 fn f<'a>(&'a mut self) {} }
或者更简洁,利用Rust自动省略生命周期的规则(对于方法来说,&mut self的生命周期会被自动推导为独立的):
impl<'x> container<'x> { fn f(&mut self) {} }
修改后,原代码就能正常编译,a1的生命周期会被限制在代码块内部,不会再“逃逸”。
内容的提问来源于stack exchange,提问作者Wilson Luniz
相关产品推荐
相关产品推荐

