Code Fix

上級

already borrowed: BorrowMutError パニックの原因と直し方

RefCellは借用規則を実行時にチェックする仕組みです。borrow_mut()で可変借用した値がまだ生きている間に、もう一度borrow_mut()やborrow()を呼ぶとパニックします。

エラーメッセージの読み方

thread 'main' panicked at main.rs:6:19:
main.rs
ファイル名
6
行番号 — 実際にクラッシュした行
19
列番号
panicked
パニック — 実行時に回復不能な状態になったことを示します。tryやResultで捕まえない限り、そのままプロセスが終了します
already borrowed: BorrowMutError
詳細メッセージ — どの値が問題だったか

このエラーが出る典型パターン

パターン1

 1  use std::cell::RefCell;
 2  fn main() {
 3      let data = RefCell::new(5);
 4      let b1 = data.borrow_mut();
 5               
            ^
 6      let b2 = data.borrow_mut();
 7      println!("{}", b2);
 8  }
thread 'main' panicked at main.rs:6:19: already borrowed: BorrowMutError

b1が生きたまま2回目のborrow_mut()を呼ぶとパニックします。dropで先に借用を終了させる必要があります。

直し方: (空)drop(b1); にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  use std::cell::RefCell;
 2  fn main() {
 3      let counter = RefCell::new(0);
 4      let c1 = counter.borrow_mut();
 5               
            ^
 6      let c2 = counter.borrow_mut();
 7      println!("{}", c2);
 8  }
thread 'main' panicked at main.rs:6:22: already borrowed: BorrowMutError

RefCellは借用ルールを実行時にチェックするため、違反すればコンパイルは通っても実行時にパニックします。

直し方: (空)drop(c1); にします。

この問題を解いてみる →

パターン3

 1  use std::cell::RefCell;
 2  fn main() {
 3      let cache = RefCell::new(10);
 4      let x1 = cache.borrow_mut();
 5               
            ^
 6      let x2 = cache.borrow_mut();
 7      println!("{}", x2);
 8  }
thread 'main' panicked at main.rs:6:20: already borrowed: BorrowMutError

1つ目の可変借用を使い終わったら、明示的にdropして解放してから次の借用を行う必要があります。

直し方: (空)drop(x1); にします。

この問題を解いてみる →

パターン4

 1  use std::cell::RefCell;
 2  fn main() {
 3      let state = RefCell::new(1);
 4      let s1 = state.borrow_mut();
 5               
            ^
 6      let s2 = state.borrow_mut();
 7      println!("{}", s2);
 8  }
thread 'main' panicked at main.rs:6:20: already borrowed: BorrowMutError

コンパイル時の借用チェックが効かない代わりに、RefCellは違反時に確実にパニックで知らせます。

直し方: (空)drop(s1); にします。

この問題を解いてみる →

パターン5

 1  use std::cell::RefCell;
 2  fn main() {
 3      let total = RefCell::new(100);
 4      let t1 = total.borrow_mut();
 5               
            ^
 6      let t2 = total.borrow_mut();
 7      println!("{}", t2);
 8  }
thread 'main' panicked at main.rs:6:20: already borrowed: BorrowMutError

すでに可変借用されている状態でさらに借用しようとすると、already borrowedというパニックになります。

直し方: (空)drop(t1); にします。

この問題を解いてみる →

よくある誤解

「RefCellを使えば、借用ルールを気にせず何回でも借用できるはず」という思い込みは誤りです。RefCellはコンパイル時ではなく実行時に借用ルールをチェックする仕組みで、違反すればその場でパニックします。

まとめ

already borrowed: BorrowMutError パニックの原因と直し方は上級でつまずきやすい項目です。上の5パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)