コンテンツにスキップ

Rustのエラー E0596: cannot borrow as mutable, as it is not declared as mutable(mut忘れ)

E0596 は、あるコードが値を変更しようとしている(つまり可変の借用 &mut が必要な)のに、あなたが読む許可しか与えていない、という意味のエラーです。たいていは let に mut を付け忘れたか、&mut が必要な関数が & を受け取っているかのどちらかです。とてもよく出るエラーですが、コンパイラの help: の行が、どこに mut を足せばいいかをほぼ正確に教えてくれます。このページでは、まず直し方を見せてから、その理由を説明します。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。

error[E0596]: cannot borrow `tasks` as mutable, as it is not declared as mutable
--> src/main.rs:3:5
|
3 | tasks.push("write tests"); // ❌ push は `tasks` を変更する必要がある
| ^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
2 | let mut tasks = Vec::new();
| +++

「tasks は可変として宣言されていないので、可変として借用できません」という意味です。help は「これを可変に変えてはどうか」と提案しています。

このエラーが出るのは、次のプログラムです。

fn main() {
let tasks = Vec::new();
tasks.push("write tests"); // ❌ push は `tasks` を変更する必要がある
println!("{tasks:?}");
}

もうひとつの文言、「cannot borrow *tasks as mutable, as it is behind a & reference」(& 参照の先にあるので、可変として借用できない)もよく見かけます。エラーコードは同じで、原因が少しだけ違います。こちらは変数が & 参照なので、参照先の値を変えられない、という場合です。どちらもこのページで扱います。

3行目の tasks の下にある ^^^^^ を見てください。要素を追加するには、Vec::push がベクタを可変で借用する必要があります。シグネチャが fn push(&mut self, value: T) だからです。ところが tasks はただの let で作られたので、読み取り専用です。「絶対に変わらない」と言われたものに、Rust は &mut を渡せません。

help: の行に直し方が書いてあります。let mut tasks です。+++ は、足すべき3文字の場所を示しています。

「閲覧のみ」の権限で共有されたドキュメントを想像してみてください。どの文字も読めますが、「編集」ボタンはグレーになっています。mut(または &mut)は、編集の権限をお願いする方法なのです。

Rust では、変数はデフォルトで不変、参照もデフォルトで読み取り専用です。何かを変えるには、はっきり宣言する必要があります。自分が所有する変数なら let mut、借りているものへの参照なら &mut です。変数と借用のページも見てください。

これのおかげで、コードを信頼しやすくなります。関数が &Vec<String> を受け取るなら、シグネチャを見ただけで、その関数はリストを変えないとわかります。&mut Vec<String> を受け取るなら、変えるかもしれないとわかります。知らないうちに何かが変わっている、ということがありません。

同じルールが、Rust のもっと大きな安全性の保証も支えています。「変更できる」ことがいつも明記されているので、コンパイラは、書く人と読む人が同時に存在しないことを確かめられるのです(それが E0502 と E0499 です)。E0596 はその最初の一歩です。書き込んでも安全かを誰かがチェックする前に、まず書き込みたいと申し出る必要があるのです。

自分が値を所有していて、その値が変わるべきときに使います。要素を push する Vec、文字を追加する String、&mut self のメソッドを呼ぶ構造体などです。たいていはこれが正解です。

struct Counter {
count: u32,
}
impl Counter {
fn increment(&mut self) {
self.count += 1;
}
}
fn main() {
let mut tasks = Vec::new();
tasks.push("テストを書く");
tasks.push("バグを直す");
println!("{tasks:?}");
let mut counter = Counter { count: 0 };
counter.increment();
counter.increment();
println!("カウント: {}", counter.count);
}

実行結果:

["テストを書く", "バグを直す"]
カウント: 2
▶ 実際に試してみよう

counter に mut がないと、counter.increment() は cannot borrow counter as mutable で失敗します。&mut self を受け取るメソッドを呼ぶと、変数全体が可変で借用されるので、変数が mut でなければならないのです。&self しか受け取らないメソッドなら、mut がなくても問題なく呼べます。メソッドで振る舞いを追加するも見てください。

関数が所有している引数も同じです。fn shout(s: String) の中では s.push('!') を呼べませんが、fn shout(mut s: String) なら呼べます。

直し方2:引数を &mut にして、&mut x を渡す

Section titled “直し方2:引数を &mut にして、&mut x を渡す”

関数が、呼び出し側の所有する値を変える必要があるときに使います。この形だと、「behind a & reference」のほうの文言になります。

fn add_task(tasks: &Vec<String>, task: &str) {
tasks.push(task.to_string()); // ❌ `tasks` は `&` 参照
}
error[E0596]: cannot borrow `*tasks` as mutable, as it is behind a `&` reference
--> src/main.rs:2:5
|
2 | tasks.push(task.to_string()); // ❌ `tasks` は `&` 参照
| ^^^^^ `tasks` is a `&` reference, so it cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
1 | fn add_task(tasks: &mut Vec<String>, task: &str) {
| +++

「tasks は & 参照なので、可変として借用できません」という意味で、help は引数を可変参照(&mut)に変えるよう提案しています。

直す場所は3か所です。引数の型を &mut Vec<String> にし、呼び出し側の変数を let mut にし、呼び出すときに &mut tasks を渡します。

fn add_task(tasks: &mut Vec<String>, task: &str) {
tasks.push(task.to_string());
}
fn main() {
let mut tasks = Vec::new();
add_task(&mut tasks, "テストを書く");
add_task(&mut tasks, "ドキュメントを更新する");
println!("タスク{}件: {tasks:?}", tasks.len());
}

実行結果:

タスク2件: ["テストを書く", "ドキュメントを更新する"]
▶ 実際に試してみよう

(ここでの tasks.len() は文字列の長さではなく、ベクタの要素数なので、タスク名を日本語にしても結果は変わりません。)

引数の型だけ変えて、&tasks を渡したままだと、代わりに E0308 が出ます。types differ in mutability(型の可変性が違う)という注記付きです。これは単に「呼び出し側でも &mut tasks と書いてね」という意味です。呼び出し側で &mut と書かせるのはわざとです。main を読む人が誰でも、add_task が tasks を変えるかもしれないと気づけるからです。

直し方3:クロージャの変数を mut にする

Section titled “直し方3:クロージャの変数を mut にする”

クロージャがキャプチャした変数を変更するときに使います。そういうクロージャは、実行するたびに自分の状態を変えるので、呼び出すにはクロージャ自身を可変で借用する必要があります。

fn main() {
let mut clicks = 0;
let click = || clicks += 1;
click(); // ❌ cannot borrow `click` as mutable
click();
println!("Clicks: {clicks}");
}
error[E0596]: cannot borrow `click` as mutable, as it is not declared as mutable
--> src/main.rs:3:9
|
3 | let click = || clicks += 1;
| ^^^^^ ------ calling `click` requires mutable binding due to mutable borrow of `clicks`
| |
| not mutable
4 | click(); // ❌ cannot borrow `click` as mutable
| ----- cannot borrow as mutable
5 | click();
| ----- cannot borrow as mutable
|
help: consider changing this to be mutable
|
3 | let mut click = || clicks += 1;
| +++

ラベルは「clicks を可変で借用しているので、click を呼ぶには可変の束縛が必要」と言っています。

変数 clicks はもう mut になっています。足りないのは、クロージャのほうの mut です。

fn main() {
let mut clicks = 0;
let mut click = || clicks += 1;
click();
click();
click();
println!("クリック数: {clicks}");
}

実行結果:

クリック数: 3
▶ 実際に試してみよう

これには驚く人が多いです。クロージャは、見た目では何かを変えているように「見えない」からです。でも中に &mut clicks を持っているので、これは FnMut のクロージャで、FnMut を呼ぶには mut が必要なのです。キャプチャした変数を変更するクロージャと Fn・FnMut・FnOnce も見てください。

直し方4:ループで iter_mut() や &mut を使う

Section titled “直し方4:ループで iter_mut() や &mut を使う”

コレクションをループして、それぞれの要素を変えたいときに使います。.iter() や for x in &list が渡すのは & 参照なので、読み取り専用です。

for name in names.iter() {
name.push('!'); // ❌ cannot borrow `*name` as mutable, as it is behind a `&` reference
}

.iter_mut() か &mut names に切り替えます(names 自体も mut にします)。

fn main() {
let mut names = vec![String::from("ferris"), String::from("corro")];
for name in names.iter_mut() {
name.push('!');
}
println!("{names:?}");
let mut prices = vec![10, 20, 30];
for price in &mut prices {
*price += 5;
}
println!("{prices:?}");
}

実行結果:

["ferris!", "corro!"]
[15, 25, 35]
▶ 実際に試してみよう

コンパイラもこれを提案してくれます(help: use mutable method ... names.iter_mut())。イテレータのチェーンの中のクロージャも同じです。words.iter().for_each(|w| w.push('!')) は失敗し、words.iter_mut().for_each(|w| w.push('!')) なら動きます。イテレータも見てください。

Vec や String を少しずつ組み立てるとき。 let list = Vec::new(); のあとに list.push(...)、または let s = String::new(); のあとに s.push_str(...) と書いた場合です。直し方:let mut にします。最初から全部の要素がそろっているなら、let list = vec![a, b, c]; と書けば mut はまったく要りません。

構造体の「セッター」や「更新」メソッドを呼ぶとき。 deposit が &mut self を受け取るなら、let mut account と書いていない限り account.deposit(50) は失敗します。直し方:let mut account = Account::new(); にします。構造体も見てください。

ついクセで & を受け取ってしまうヘルパー関数。 fn reset(board: &Board) と書いて、中で board を変えようとした場合です。直し方:board: &mut Board に変えて、reset(&mut board) と呼びます。読むだけの関数には &、変える関数には &mut と書く習慣をつけるといいですよ。

HashMap の中の値を変えるとき。 if let Some(v) = map.get("key") { v.push(..) } は失敗します。get が返すのは & 参照だからです。直し方:map.get_mut("key") か、entry API を使います。HashMap も見てください。

  • E0384:cannot assign twice to immutable variable(不変の変数に2回代入できない)。同じ mut の付け忘れですが、メソッド呼び出しではなく x = ... や x += 1 のときに出ます。
  • E0594:フィールドや & 参照の先に代入できない。たとえば p が mut でないのに p.hp = 5; と書いたときや、for x in &v の中で *x += 1 と書いたときです。直し方は同じく let mut か &mut です。
  • E0308:mismatched types(型の不一致)。&mut x がほしい関数に &x を渡すと、types differ in mutability と言われます。
  • E0502:&mut で借用できたものの、別の場所がまだその値を読んでいる。
  • E0499:同じ値を同時に2回 &mut で借用した。
  • コンパイルエラーの一覧:エラー解説ページの一覧です。

理解度チェック

1. let scores = Vec::new(); scores.push(10); で E0596 が出ました。どう直しますか?

2. 関数 fn add(list: &Vec<u32>) の中で list.push(1) を呼んでいます。何を変えればいいですか?

3. let mut total = 0; let add = |x| total += x; add(5); が失敗します。なぜでしょうか?

スコア: 0 / 3