此代码使可怕的借阅检查器(playground)失败:
struct Data {
a: i32,
b: i32,
c: i32,
}
impl Data {
fn reference_to_a(&mut self) -> &i32 {
self.c = 1;
&self.a
}
fn get_b(&self) -> i32 {
self.b
}
}
fn main() {
let mut dat = Data{ a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
println!("{}", dat.get_b());
}
由于实现了非词法生存期,因此触发该错误是必需的:
fn main() {
let mut dat = Data { a: 1, b: 2, c: 3 };
let aref = dat.reference_to_a();
let b = dat.get_b();
println!("{:?}, {}", aref, b);
}
错误:
error[E0502]: cannot borrow `dat` as immutable because it is also borrowed as mutable
--> <anon>:19:20
|
18 | let aref = dat.reference_to_a();
| --- mutable borrow occurs here
19 | println!("{}", dat.get_b());
| ^^^ immutable borrow occurs here
20 | }
| - mutable borrow ends here
为什么?我本以为dat
的可变借位在返回reference_to_a()
时会转换为不可变的借位,因为该函数仅返回不可变的引用。借钱检查员还不够聪明吗?这有计划吗?有没有解决的办法?
生存期与引用是否可变无关。通过代码进行工作:
fn reference_to_a(&mut self) -> &i32
尽管已删除生命周期,但这等效于:
fn reference_to_a<'a>(&'a mut self) -> &'a i32
即输入和输出寿命是相同的。这是将生存期分配给这样的函数的唯一方法(除非它返回对全局数据的&'static
引用),因为您不能从零开始弥补输出生存期。
这意味着,如果通过将返回值保存在变量中来使返回值保持活动状态,则也使&mut self
保持活动状态。
另一种思考方式是&i32
是&mut self
的子借项,因此仅在到期之前有效。
正如@aSpex指出的,这是covered in the nomicon。
为什么会出错:尽管2.5年前@Chris已经给出了更精确的解释,但是您可以将fn reference_to_a(&mut self) -> &i32
读为声明:
仍然无法表达“我想在通话期间专门借用我自己,并返回带有单独生命周期的共享引用”。正如@aSpex所指出的,它是it can even prevent me from shooting myself in the foot,并且在2018年末被列在mentioned in the nomicon中。“我想排他地借用
self
,然后返回一个共享的/不可变的引用,该引用的生存期与原始排他期一样长”显然是source。借阅检查器还不够聪明吗?这有计划吗?
我无法像以前Things Rust doesn’t let you do那样找到解决这个问题的具体计划。关于允许单独的读/写“生命周期角色”(other borrow checker improvements were deemed higher priority)的想法是Ref2<'r, 'w>
,但据我所知,没有人将其变成自己的RFC。
是否有解决方法?
并非如此,但是首先取决于您需要使用此代码的原因,可以使用其他结构化代码的方法:您可以返回副本/克隆而不是引用fn(&mut self) -> &T
,另一个取回&mut self
,&T
fn(&mut self) -> (&Self, &T)
)&self
(即,将interior mutability的需要定义为Self
或Cell<T>
的部分定义)。这可能看起来像作弊,但这实际上是适当的,例如当您需要可变性为RefCell<T>
的原因时。毕竟,我们正在制定一种将implementation detail of a logically-immutable method &mut self
设置为not because it mutates parts of self
, but的方法。