此代码正常(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 | | }
| |_____^
为什么会这样?据我所知,生命周期的任何内容都没有改变:所有的值/引用仍然与第一个代码片段完全一样长。
答案 0 :(得分:5)
为什么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
两个指向x
。
注意:要进行编译,您可以在unsafe
内使用get
:unsafe { std::mem::transmute(&*self.x) }
;吓人,嗯?
借用检查算法的核心是Rust内存安全性的基石:
别名XOR可变性
Rust通过保证每当你修改某些内容时,没有任何垃圾收集来实现内存安全,没有任何观察者可以在内部引用可能会变成悬空的内容。
这反过来让我们解释:
&T
作为别名参考;它是Copy
&mut T
作为唯一参考;它不是Copy
,因为它会违反唯一性,但可以移动这种差异在这里拯救了我们。
由于无法复制&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
。