Rust中结构体能否存储生命周期长于自身的引用?
我想创建一个生命周期较短的结构体,其中包含对生命周期更长的数据的引用(示例省略了其他元数据)。当这个短生命周期结构体存在时,我希望能提取出这个长生命周期的引用,但rustc始终不接受我的代码。这从根本上是否不可能?如果是,原因是什么?
示例代码
struct Foo{} struct HasRef<'a>(&'a mut Foo); impl<'a> HasRef<'a> { fn get_ref(&mut self) -> &'a mut Foo { self.0 } fn alt_get_ref<'b>(&'b mut self) -> &'a mut Foo where 'a: 'b { self.0 } } fn main() { let mut f = Foo {}; let mut h = HasRef(&mut f); h.get_ref(); }
编译错误信息
Compiling playground v0.0.1 (/playground) error: lifetime may not live long enough --> src/lib.rs:7:9 | 5 | impl<'a> HasRef<'a> { | -- lifetime `'a` defined here 6 | fn get_ref(&mut self) -> &'a mut Foo { | - let's call the lifetime of this reference `'1` 7 | self.0 | ^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1` error: lifetime may not live long enough --> src/lib.rs:13:9 | 5 | impl<'a> HasRef<'a> { | -- lifetime `'a` defined here ... 10 | fn alt_get_ref<'b>(&'b mut self) -> &'a mut Foo | -- lifetime `'b` defined here ... 13 | self.0 | ^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | = help: consider adding the following bound: `'b: 'a` error: could not compile `playground` (lib) due to 2 previous errors
解答
核心结论:这在Rust中不可能实现
原因在于Rust的借用规则必须严格保证内存安全,你的需求直接违反了两条核心规则:
可变引用的唯一性
当你通过&mut self借用结构体时,Rust会暂时将结构体的可变权限交给这个借用。如果允许你直接返回内部的&'a mut Foo,就会同时存在两个指向同一数据的可变引用:一个是&mut self(控制结构体),另一个是返回的&'a mut Foo(控制原始数据)。这完全违反了Rust"同一时间只能有一个可变引用"的规则,会导致数据竞争风险。防止悬垂引用的生命周期约束
编译器会默认推断:从&mut self中取出的引用,其生命周期不能超过self本身。如果强制返回更长的'a生命周期,编译器无法保证在结构体被销毁后,返回的引用不会变成悬垂引用——尽管逻辑上原始数据生命周期更长,但编译器不会为你做这种"特殊情况"的豁免,因为这会破坏整个类型系统的安全性。
正确的替代方案
如果你确实需要获取原始数据的长生命周期引用,有两种合规的方式:
1. 消耗结构体,取出内部引用
通过into_inner方法销毁结构体,同时取出内部的长生命周期引用,这样就不会有双重可变引用的问题:
struct Foo{} struct HasRef<'a>(&'a mut Foo); impl<'a> HasRef<'a> { fn into_inner(self) -> &'a mut Foo { self.0 } } fn main() { let mut f = Foo {}; let h = HasRef(&mut f); // 结构体被消耗,返回的引用拥有原始数据的生命周期 let long_ref = h.into_inner(); // 可以正常使用long_ref,此时h已不存在 }
2. 返回与结构体借用周期绑定的引用
如果需要结构体继续存在,只能返回和&mut self生命周期绑定的引用,确保引用不会逃逸出结构体的借用范围:
impl<'a> HasRef<'a> { fn get_ref(&mut self) -> &mut Foo { self.0 } }
此时返回的引用生命周期和结构体的借用周期一致,既保证了可变引用的唯一性,也不会产生悬垂引用风险。
内容的提问来源于stack exchange,提问作者ajp

