MeWrite Docs

cannot borrow as mutable because it is also borrowed as immutable

Rustの借用チェッカーによるエラーの解決方法

概要

Rustの借用チェッカー(borrow checker)は、メモリ安全性を保証するためのコンパイル時チェック機構です。所有権と借用のルールに違反するとコンパイルエラーになります。

エラーメッセージ

error[E0502]: cannot borrow `vec` as mutable because it is also borrowed as immutable
  --> src/main.rs:5:5
   |
3  |     let first = &vec[0];
   |                  --- immutable borrow occurs here
4  |
5  |     vec.push(4);
   |     ^^^^^^^^^^^ mutable borrow occurs here
6  |
7  |     println!("{}", first);
   |                    ----- immutable borrow later used here
error[E0499]: cannot borrow `x` as mutable more than once at a time

借用のルール

  1. 複数の不変参照(&T) は同時に存在できる
  2. 可変参照(&mut T) は同時に1つだけ
  3. 不変参照と可変参照 は同時に存在できない
  4. 参照は所有者より長く生存できない

よくあるエラーと解決策

1. 不変参照と可変参照の競合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// NG: 不変参照がある間に可変操作
let mut vec = vec![1, 2, 3];
let first = &vec[0];  // 不変参照
vec.push(4);          // 可変操作 - エラー
println!("{}", first);

// OK: 不変参照のスコープを終わらせる
let mut vec = vec![1, 2, 3];
let first = vec[0];   // コピー
vec.push(4);
println!("{}", first);

// OK: 操作順序を変更
let mut vec = vec![1, 2, 3];
vec.push(4);
let first = &vec[0];
println!("{}", first);

2. ループ中の変更

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// NG: イテレート中に変更
let mut vec = vec![1, 2, 3];
for item in &vec {
    if *item == 2 {
        vec.push(4);  // エラー
    }
}

// OK: インデックスでループ
let mut vec = vec![1, 2, 3];
let mut i = 0;
while i < vec.len() {
    if vec[i] == 2 {
        vec.push(4);
    }
    i += 1;
}

// OK: retain/filter
let mut vec = vec![1, 2, 3, 4, 5];
vec.retain(|x| *x != 2);

3. 構造体のフィールドを同時に借用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// NG: 構造体全体を可変借用
struct Data {
    a: String,
    b: String,
}

let mut data = Data { a: "a".to_string(), b: "b".to_string() };
let a_ref = &mut data.a;
let b_ref = &mut data.b;  // エラー: dataを2回可変借用

// OK: 分割借用(split borrowing)
let Data { a, b } = &mut data;
// aとbは別々に可変借用可能

4. 関数からの参照返却

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// NG: ローカル変数への参照を返す
fn get_string() -> &String {
    let s = String::from("hello");
    &s  // エラー: sはこの関数で破棄される
}

// OK: 所有権を移動
fn get_string() -> String {
    String::from("hello")
}

// OK: 引数からの参照を返す
fn get_first(strings: &[String]) -> Option<&String> {
    strings.first()
}

5. クロージャと借用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// NG: クロージャが不変借用、外で可変操作
let mut vec = vec![1, 2, 3];
let closure = || vec.len();  // 不変借用
vec.push(4);  // エラー
closure();

// OK: クロージャを先に使う
let mut vec = vec![1, 2, 3];
let len = vec.len();
vec.push(4);
println!("{}", len);

// OK: moveクロージャ
let vec = vec![1, 2, 3];
let closure = move || vec.len();  // 所有権を移動

解決パターン

1. Cloneを使う

1
2
3
4
let mut vec = vec![1, 2, 3];
let first = vec[0].clone();  // コピー
vec.push(4);
println!("{}", first);

2. スコープを分ける

1
2
3
4
5
6
let mut vec = vec![1, 2, 3];
{
    let first = &vec[0];
    println!("{}", first);
}  // firstのスコープ終了
vec.push(4);

3. RefCell(実行時借用チェック)

1
2
3
4
5
6
7
use std::cell::RefCell;

let vec = RefCell::new(vec![1, 2, 3]);
{
    let first = vec.borrow()[0];  // 実行時に借用チェック
    vec.borrow_mut().push(4);     // パニックの可能性
}

4. Rc<RefCell>(共有可変参照)

1
2
3
4
5
6
7
8
use std::rc::Rc;
use std::cell::RefCell;

let shared = Rc::new(RefCell::new(vec![1, 2, 3]));
let shared_clone = Rc::clone(&shared);

shared.borrow_mut().push(4);
println!("{:?}", shared_clone.borrow());

5. 内部可変性パターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use std::cell::Cell;

struct Counter {
    count: Cell<u32>,  // 内部可変性
}

impl Counter {
    fn increment(&self) {  // &selfでも変更可能
        self.count.set(self.count.get() + 1);
    }
}

ライフタイムエラー

1
2
3
4
5
6
7
8
9
// NG: ライフタイムが曖昧
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}

// OK: ライフタイム注釈
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

デバッグのコツ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// エラー箇所を特定
// コンパイラのエラーメッセージを注意深く読む
// - "borrow occurs here" で借用箇所を確認
// - "borrow later used here" で使用箇所を確認

// 最小限の再現コードを作成
fn main() {
    let mut x = vec![1, 2, 3];
    let y = &x[0];
    x.push(4);
    println!("{}", y);
}

関連エラー

関連エラー

Rust の他のエラー

最終更新: 2025-12-23