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

Fel*_*ter 15 immutability mutability rust

首先,让代码说:

#[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();
}
Run Code Online (Sandbox Code Playgroud)

这个错误:

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
Run Code Online (Sandbox Code Playgroud)

如果我取消或者2.还是3.,为什么它编译就好了?被调用的函数1.没有做任何与2.or 完全不同的事情3..那么为什么1.不能编译呢?

虽然有许多类似的标题问题,但我无法清楚地将其识别为欺骗(除了错误信息相同),可能是因为我对Rust中的所有权/借用系统缺乏了解.

She*_*ter 10

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

fn get_qux(&mut self) -> &mut Qux {
    &mut self.qux
}
Run Code Online (Sandbox Code Playgroud)

这个函数表明所有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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,人工范围明确定义了可变借款的结束位置.借款结束后,其他项目可以进行新借款.

如果需要qux在循环内部进行修改,则需要遵循第三种模式:

let mut qux = &mut self.qux;

for bar in &self.bars {
    qux.baz = ! qux.baz;
    println!("{:?}", bar);
}
Run Code Online (Sandbox Code Playgroud)

或者更简单:

for bar in &self.bars {
    self.qux.baz = ! self.qux.baz;
    println!("{:?}", bar);
}
Run Code Online (Sandbox Code Playgroud)

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