首先,让代码说出来:
#[derive(Debug)]
struct Bar;
#[derive(Debug)]
struct Qux {
baz: bool
}
#[derive(Debug)]
struct Foo {
bars: Vec<Bar>,
qux: Qux,
}
impl Foo {
fn get_qux(&mut self) -> &mut Qux {
&mut self.qux
}
fn run(&mut self) {
// 1. Fails:
let mut qux = self.get_qux();
// 2. Works:
// let mut qux = &mut Qux { baz: false };
// 3. Works:
// let mut qux = &mut self.qux;
let qux_mut = &mut qux;
qux_mut.baz = true;
for bar in &self.bars {
println!("{:?}", bar);
}
}
}
fn main() {
println!("Hello, world!");
let mut foo = Foo { bars: vec!(), qux: Qux { baz: false } };
foo.run();
}
此错误:
error[E0502]: cannot borrow `self.bars` as immutable because `*self` is also borrowed as mutable
--> src/main.rs:33:21
|
22 | let mut qux = self.get_qux();
| ---- mutable borrow occurs here
...
33 | for bar in &self.bars {
| ^^^^^^^^^ immutable borrow occurs here
...
36 | }
| - mutable borrow ends here
如果我取消注释2.
或3.
,为什么编译得很好? 1.
中的被调用函数不会执行与2.
或3.
完全不同的任何操作。那么为什么1.
无法编译呢?
虽然有many similar titled questions,但我无法清楚地将其识别为欺骗(除了错误信息相同),可能是因为我对Rust中的所有权/借用系统缺乏了解。
答案 0 :(得分:9)
在Rust中,编译器在评估泛型参数(包括通用生命周期参数)时停止在函数调用边界处。在您的情况1中,您正在调用方法:
fn get_qux(&mut self) -> &mut Qux {
&mut self.qux
}
此函数表示{em>所有 self
将被可变地借用,并且只要self
将返回引用。在此期间,没有其他借用(可变或不可能)自我或其组成部分。
在第二种情况下,你构成了一个全新的Qux
,它与你的结构没有任何关联。它不是一个非常好的例子,因为它有着非常不同的含义。 如果此案例适合您,则应该这样做。但是,您不会修改与案例1相同的内容。
在第三种情况下,您可以避免函数调用。这意味着编译器有更多关于确切借用的信息。具体来说,它可以看到self.qux
与self.bars
完全没有互动,因此没有错误。
您可以通过添加新范围来使原始示例正常工作:
fn run(&mut self) {
{
let mut qux = self.get_qux();
let qux_mut = &mut qux;
qux_mut.baz = true;
}
for bar in &self.bars {
println!("{:?}", bar);
}
}
在这里,人工范围明确定义了可变借款的结束位置。借款结束后,其他项目可以进行新借款。
如果你需要在循环中修改qux
,那么你需要遵循第三种模式:
let mut qux = &mut self.qux;
for bar in &self.bars {
qux.baz = ! qux.baz;
println!("{:?}", bar);
}
或者更简单:
for bar in &self.bars {
self.qux.baz = ! self.qux.baz;
println!("{:?}", bar);
}
很多时候,您可以重构代码以创建具有信息的新结构,并封装一个很好的变异边界来生成这样的代码。