Не может считать `self.x` неизменным, потому что` * self` также заимствован как изменяемый

Во-первых, пусть код говорит:

#[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. не удается скомпилировать?

Хотя есть много похожих названных вопросов, я не мог четко определить это как обман (за исключением сообщения об ошибке, являющегося тем же), возможно из-за моего отсутствия понимания системы собственности/заимствования в Rust.

Ответ 1

В Rust компилятор останавливается на границе вызова функции при оценке общих параметров, который включает в себя общие параметры времени жизни. В вашем случае 1 вы вызываете метод:

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

Эта функция указывает, что все 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);
}

Много раз вы можете реорганизовать свой код для создания новых структур, которые имеют информацию и инкапсулируют красивую границу мутации, чтобы сделать такой код.