Rust中结构体持有可变引用字段时的生命周期与借用错误问题
我太懂你现在的懵圈感了——明明只是把不可变引用换成可变的,怎么就突然炸出一堆错误?而且被要求给方法加&mut self,感觉完全没必要啊,毕竟你只是想把内部的可变引用原封不动返回出去,根本没改Ref结构体本身对吧?别急,咱们一步步拆解这些问题:
先搞懂为什么不可变引用版本能正常工作
在第一个例子里,Ref持有&'s Data,你用&self就能返回&'s Data,这是因为不可变引用是共享安全的:Rust允许多个不可变引用同时存在,所以编译器可以放心地把内部引用的生命周期's和返回值绑定,不会有数据竞争或者悬垂引用的风险。而且不可变引用的生命周期是协变的——简单说就是编译器可以自动调整生命周期的长度来满足需求,所以不会出现生命周期不匹配的问题。
可变引用版本的两个错误到底在说什么
咱们先看第二个错误(也是更直观的那个):
error[E0596]: cannot borrow
*self.xas mutable, as it is behind a&reference
这个错误的核心是Rust的可变引用独占规则:如果你持有一个值的不可变引用(也就是这里的&self),你就不能通过它获取任何内部的可变引用——因为可变引用要求绝对独占,要是允许通过&self拿到&mut Data,那你可以同时创建多个&Ref实例,每个都能拿到同一个Data的可变引用,这直接违反了Rust最核心的内存安全规则。
再看第一个生命周期错误:
error: lifetime may not live long enough -- method was supposed to return data with lifetime
'sbut it is returning data with lifetime'1
这里的'1是&self的生命周期,编译器认为你从&self里取出的self.x,它的生命周期只能和&self一样长(也就是'1),而不是你定义的's。为什么?因为可变引用是独占的,编译器必须确保你返回的可变引用不会比借用的self活得更久——否则就可能出现悬垂引用(比如self被销毁了,但返回的&mut Data还在被使用)。而不可变引用因为是共享的,编译器允许这种生命周期的“延长”,可变引用则不行。
为什么加&mut self后第二个错误消失,但第一个还在?
加&mut self之后,你拿到了Ref的可变借用,这时候编译器允许你获取内部的可变引用了(因为可变借用本身是独占的,不会出现多个可变引用共存的问题),所以第二个错误消失了。但第一个错误还在,是因为编译器默认会把返回的引用和&mut self的生命周期('1)绑定,而不是结构体的's。
那怎么解决这个问题?分两种情况:
- 如果你愿意消耗掉
Ref结构体:可以把方法改成接收self而不是&self/&mut self,这样就能直接返回&'s mut Data,因为你转移了结构体的所有权,内部的可变引用的生命周期's会被完整保留:struct Data; struct Ref<'s> { x: &'s mut Data, } impl<'s> Ref<'s> { pub fn into_ref(self) -> &'s mut Data { self.x } } - 如果你不想消耗
Ref:那必须用&mut self,而且要接受返回的引用的生命周期和&mut self绑定——或者说,这其实是合理的,因为只要你持有&mut Ref,你就独占了内部的&mut Data,返回的引用的生命周期自然不能超过&mut self的生命周期,这是Rust在帮你保证内存安全。当然,如果你确实需要返回&'s mut Data,那只能选择消耗结构体的方式。
最后再总结一下核心逻辑
可变引用的规则比不可变引用严格得多,因为它涉及到独占性:
- 不可变引用可以通过不可变借用(
&self)共享,编译器允许生命周期的灵活调整 - 可变引用必须独占,所以要么消耗结构体来转移内部可变引用的所有权,要么通过可变借用(
&mut self)来获取,并且返回的引用生命周期会被绑定到可变借用的生命周期
内容来源于stack exchange

