できない.dev

Rust で「cannot borrow as mutable more than once」(E0499) が解決できない

同じ値に対する可変参照は同時に 1 つしか持てない。
E0499 は 2 つ目の &mut を作ったときに出る。
借用のスコープを分ける、インデックスで扱う、split_at_mutRefCell を使う、のいずれかで解決する。

公開:

要約

E0499 は、同じ値に対する可変参照を 2 つ同時に作ったときに出る。
Rust の借用規則では「不変参照は何個でも、可変参照は 1 つだけ」で、この 2 つは同時に成立しない。

let mut i = 0;
let x = &mut i;
let a = &mut i;   // error[E0499]: cannot borrow `i` as mutable more than once at a time
*x += 1;

借用が「いつまで生きているか」で判定されるため、2 つ目を作った行が悪いとは限らない。
1 つ目を最後に使う位置を動かすだけで通ることも多い。

よくある原因

  1. 借用が重なっている: 1 つ目の &mut を後の行で使っていると、その時点まで借用が続く。
    間に 2 つ目を作れば衝突する。
  2. 借りたままコレクションを変更している: 要素への &mut を持ったまま push すると、再確保で参照が無効になりうるためコンパイラが止める。
  3. 同じ Vec の 2 要素を同時に取ろうとしている: &mut v[0]&mut v[1] は、コンパイラからは同じ v への二重借用に見える。
  4. &mut self メソッドの二重呼び出し: 引数の中で自分自身の可変メソッドを呼ぶと重なる。

解決策

1. 借用のスコープを分ける

let mut i = 0;
{
    let x = &mut i;
    *x += 1;
}          // ここで x の借用が終わる
let a = &mut i;
*a += 1;

明示的なブロックを使わなくても、1 つ目を使い終えた後なら 2 つ目を作れる。
借用は最後の使用位置までしか続かない。

2. 参照ではなくインデックスや値で扱う

let mut v = vec![1, 2, 3];
let first = v[0];        // i32 は Copy なので借用が残らない
v.push(first + 10);

3. 2 要素同時なら split_at_mut を使う

let mut v = vec![1, 2, 3, 4];
let (left, right) = v.split_at_mut(2);
left[0] += right[0];

標準ライブラリが「重ならないこと」を保証してくれるため、二重借用にならない。

4. どうしても共有が要るなら RefCell

use std::cell::RefCell;
 
let cell = RefCell::new(0);
*cell.borrow_mut() += 1;

借用チェックがコンパイル時から実行時に移るだけなので、重なればパニックする。
設計を見直せるならまず 1 〜 3 を試す。
詳細は参照と借用の章(新しいタブで開く)にある。

この記事は役立ちましたか?