不能将`self.x`借用为不可变因为`* self`也被借用为可变的

时间:2015-06-27 21:45:56

标签: rust immutability mutability

首先,让代码说出来:

#[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中的所有权/借用系统缺乏了解。

1 个答案:

答案 0 :(得分:9)

在Rust中,编译器在评估泛型参数(包括通用生命周期参数)时停止在函数调用边界处。在您的情况1中,您正在调用方法:

fn get_qux(&mut self) -> &mut Qux {
    &mut self.qux
}

此函数表示{em>所有 self将被可变地借用,并且只要self将返回引用。在此期间,没有其他借用(可变或不可能)自我或其组成部分。

在第二种情况下,你构成了一个全新的Qux,它与你的结构没有任何关联。它不是一个非常好的例子,因为它有着非常不同的含义。 如果此案例适合您,则应该这样做。但是,您不会修改与案例1相同的内容。

在第三种情况下,您可以避免函数调用。这意味着编译器有更多关于确切借用的信息。具体来说,它可以看到self.quxself.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);
}

很多时候,您可以重构代码以创建具有信息的新结构,并封装一个很好的变异边界来生成这样的代码。