Не может заимствовать как неизменяемый, потому что он также заимствован как изменяемый в аргументах функции

Что здесь происходит ( детская площадка)?

struct Number {
    num: i32
}

impl Number {
    fn set(&mut self, new_num: i32) {
        self.num = new_num;
    }
    fn get(&self) -> i32 {
        self.num
    }
}

fn main() {
    let mut n = Number{ num: 0 };
    n.set(n.get() + 1);
}

Выдает эту ошибку:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
  --> <anon>:17:11
   |
17 |     n.set(n.get() + 1);
   |     -     ^          - mutable borrow ends here
   |     |     |
   |     |     immutable borrow occurs here
   |     mutable borrow occurs here

Однако, если вы просто измените код на это, он работает:

fn main() {
    let mut n = Number{ num: 0 };
    let tmp = n.get() + 1;
    n.set(tmp);
}

Для меня они выглядят абсолютно эквивалентно - я имею в виду, я ожидаю, что первое будет преобразовано во второе во время компиляции. Разве Rust не оценивает все параметры функции перед оценкой вызова функции следующего уровня?

1 ответ

Решение

Эта строка:

n.set(n.get() + 1);

обескуражен в

Number::set(&mut n, n.get() + 1);

Сообщение об ошибке теперь может быть более понятным:

error[E0502]: cannot borrow `n` as immutable because it is also borrowed as mutable
  --> <anon>:18:25
   |
18 |     Number::set(&mut n, n.get() + 1);
   |                      -  ^          - mutable borrow ends here
   |                      |  |
   |                      |  immutable borrow occurs here
   |                      mutable borrow occurs here

Поскольку Rust оценивает аргументы слева направо, этот код эквивалентен этому:

let arg1 = &mut n;
let arg2 = n.get() + 1;
Number::set(arg1, arg2);

Теперь должно быть очевидно, что не так. Поменяйте местами эти первые две строки, это исправит, но Rust не выполняет такого рода анализ потока управления.

Это было сначала создано как ошибка # 6268, теперь это интегрировано в RFC 811.

Начиная с 1.25.0-nightly 2018-01-13, можно использовать нелексические времена жизни. Если вы запустили свой пример на ночной Rust и включили функцию NLL gate, используя #![feature(nll)], теперь он будет компилироваться без ошибок.

Другие вопросы по тегам