Rustのエラー E0596: cannot borrow as mutable, as it is not declared as mutable(mut忘れ)
E0596 は、あるコードが値を変更しようとしている(つまり可変の借用 &mut が必要な)のに、あなたが読む許可しか与えていない、という意味のエラーです。たいていは let に mut を付け忘れたか、&mut が必要な関数が & を受け取っているかのどちらかです。とてもよく出るエラーですが、コンパイラの help: の行が、どこに mut を足せばいいかをほぼ正確に教えてくれます。このページでは、まず直し方を見せてから、その理由を説明します。このシリーズのほかのエラーは、Rustのコンパイルエラー解説を見てください。
エラーメッセージ
Section titled “エラーメッセージ”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」(& 参照の先にあるので、可変として借用できない)もよく見かけます。エラーコードは同じで、原因が少しだけ違います。こちらは変数が & 参照なので、参照先の値を変えられない、という場合です。どちらもこのページで扱います。
エラーの意味
Section titled “エラーの意味”3行目の tasks の下にある ^^^^^ を見てください。要素を追加するには、Vec::push がベクタを可変で借用する必要があります。シグネチャが fn push(&mut self, value: T) だからです。ところが tasks はただの let で作られたので、読み取り専用です。「絶対に変わらない」と言われたものに、Rust は &mut を渡せません。
help: の行に直し方が書いてあります。let mut tasks です。+++ は、足すべき3文字の場所を示しています。
「閲覧のみ」の権限で共有されたドキュメントを想像してみてください。どの文字も読めますが、「編集」ボタンはグレーになっています。mut(または &mut)は、編集の権限をお願いする方法なのです。
Rust がこうする理由
Section titled “Rust がこうする理由”Rust では、変数はデフォルトで不変、参照もデフォルトで読み取り専用です。何かを変えるには、はっきり宣言する必要があります。自分が所有する変数なら let mut、借りているものへの参照なら &mut です。変数と借用のページも見てください。
これのおかげで、コードを信頼しやすくなります。関数が &Vec<String> を受け取るなら、シグネチャを見ただけで、その関数はリストを変えないとわかります。&mut Vec<String> を受け取るなら、変えるかもしれないとわかります。知らないうちに何かが変わっている、ということがありません。
同じルールが、Rust のもっと大きな安全性の保証も支えています。「変更できる」ことがいつも明記されているので、コンパイラは、書く人と読む人が同時に存在しないことを確かめられるのです(それが E0502 と E0499 です)。E0596 はその最初の一歩です。書き込んでも安全かを誰かがチェックする前に、まず書き込みたいと申し出る必要があるのです。
直し方1:変数に mut を付ける
Section titled “直し方1:変数に mut を付ける”自分が値を所有していて、その値が変わるべきときに使います。要素を 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);}実行結果:
["テストを書く", "バグを直す"]カウント: 2counter に 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 mutable4 | click(); // ❌ cannot borrow `click` as mutable | ----- cannot borrow as mutable5 | 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('!')) なら動きます。イテレータも見てください。
よく出会う場面
Section titled “よく出会う場面”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 も見てください。
関連するエラー
Section titled “関連するエラー”- 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で借用した。 - コンパイルエラーの一覧:エラー解説ページの一覧です。
理解度チェック
Section titled “理解度チェック”理解度チェック
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
さらに詳しく
Section titled “さらに詳しく”- E0596 — Rust error codes index(英語):このエラーの公式の説明です。
- 参照と借用 — The Rust Programming Language 日本語版:「可変参照」の節で、
&mutとは何か、なぜ自分から求める必要があるのかを説明しています。 - メソッド記法 — The Rust Programming Language 日本語版:
&selfと&mut selfの違いと、それぞれ呼び出し側に何が必要かがわかります。 - クロージャ:環境をキャプチャする匿名関数 — The Rust Programming Language 日本語版:クロージャが変数をキャプチャするしくみと、値を変えるクロージャが
mutでなければならない理由です。 - ミュータビリティ — Rust By Example 日本語版:
&と&mutの借用を並べて比べられる、実行できる例です。