这段代码工作正常(Playground):
struct F<'a> {
x: &'a i32,
}
impl<'a> F<'a> {
fn get<'b>(&'b self) -> &'a i32 {
self.x
}
}
fn main() {
let x = 3;
let y = F { x: &x };
let z = y.get();
}
但是当我将 x
更改为可变引用时 (Playground):
struct Foo<'a> {
x: &'a mut i32, // <-- `mut` added
}
impl<'a> Foo<'a> {
fn get(&self) -> &'a i32 {
self.x
}
}
fn main() {
let mut x = 3; // <-- `mut` added
let y = Foo { x: &mut x }; // <-- `mut` added
let z = y.get();
}
我收到这个错误:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:7:9
|
7 | self.x
| ^^^^^^
|
note: ...the reference is valid for the lifetime 'a as defined on the impl at 5:6...
--> src/main.rs:5:6
|
5 | impl<'a> Foo<'a> {
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
--> src/main.rs:6:5
|
6 | / fn get(&self) -> &'a i32 {
7 | | self.x
8 | | }
| |_____^
为什么会这样?据我所知,生命周期没有任何变化:所有值/引用仍然与第一个代码片段中一样长。
最佳答案
为什么 Rust 编译器拒绝这种 get
的实现?因为它允许:
以下是一个完全合理的 main
,假设 get
编译:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
然而,如果 get
被编译,这会很好:
a
没有借用y
因为生命周期不同b
“消耗”y
(从y.x
移动)但我们不会在之后重用它
所以一切都很好,除了我们现在有一个 &i32
和 &mut i32
both 指向 x
.
注意:要使其编译,您可以在 get
中使用 unsafe
: unsafe { std::mem::transmute(&* self.x) }
;可怕吧?
借用检查算法的核心是构建 Rust 内存安全性的基石:
Aliasing XOR Mutability
Rust 通过保证无论何时修改某些内容,任何观察者都无法在可能成为悬空的内容中获得引用,从而在没有垃圾收集的情况下实现内存安全。
反过来,这让我们解释:
&T
作为别名引用;它是复制
&mut T
作为唯一引用;它不是复制
,因为它会违反唯一性,但它可以移动
这种差异拯救了我们。
由于 &mut T
无法复制,唯一的方法是从 &mut T
到 &T
(或 &mut T
) 是执行重新借用:解引用并引用结果。
这是由编译器隐式完成的。手动执行此操作会产生更好的错误消息:
fn get<'b>(&'b self) -> &'a i32 {
&*self.x
}
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements --> <anon>:7:9 | 7 | &*self.x | ^^^^^^^^ | help: consider using an explicit lifetime parameter as shown: fn get(&'a self) -> &'a i32 --> <anon>:6:5 | 6 | fn get<'b>(&'b self) -> &'a i32 { | ^
为什么它不能推断一生?因为重新借用的生命周期受 'b
限制,但我们需要一个 'a
并且两者之间没有关系!
顺便说一句,这就是避免我们犯错误的原因,因为它确保实例 Foo
必须 在结果存在时被借用(防止我们使用通过 Foo::x
的可变引用)。
按照编译器提示,返回 &'b i32
有效...并阻止上面的 main
编译:
impl<'a> Foo<'a> {
fn get<'b>(&'b self) -> &'b i32 {
&*self.x
}
}
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let a = y.get();
let b = y.x;
println!("{} {}", a, b);
}
error[E0505]: cannot move out of `y.x` because it is borrowed --> <anon>:16:9 | 15 | let a = y.get(); | - borrow of `y` occurs here 16 | let b = y.x; | ^ move out of `y.x` occurs here
但是它让第一个 main
编译没有问题:
fn main() {
let mut x = 3;
let y = Foo { x: &mut x };
let z = y.get();
println!("{}", z);
}
打印 3
。
关于rust - 当我在结构中使用可变引用而不是不可变引用时,为什么会出现生命周期错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54586997/