web-dev-qa-db-ja.com

関数の引数でも可変として借用されるため、不変として借用することはできません

ここで何が起こっているのですか( 遊び場 )?

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すべての関数パラメーターを評価しませんか?

24
Timmmm

この行:

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);

編集者注:このコード例は、根本的な問題を直感的に理解できるものですが、完全に正確ではありません。 expandedコードは、非字句の有効期間でも失敗しますが、originalコードがコンパイルされます。問題の完全な説明については、レビュー ボローチェッカーの元の実装のコメント

これで、何が問題なのかが明らかになるはずです。これらの最初の2行を入れ替えるとこれは修正されますが、Rustはそのような制御フロー分析を行いません。

これは最初に bug#6268 として作成されましたが、現在は RFC 2094non-lexical-lifetimes に統合されています。 Rust 1.36以降を使用すると、NLLが自動的に有効になり、 コードはエラーなしでコンパイルされます

25