RustエラーE0384の修正方法:不変変数への再代入不可

beginner🦀 Rust2026-03-30| Windows、macOS、またはLinux上のRustコンパイラ (rustc)。通常、Cargoを使用したコンパイルフェーズで発生します。

Error Message

error[E0384]: cannot assign twice to immutable variable `x`
#rust#コンパイラエラー#プログラミング#バックエンド

エラーの理解

Rustは安全性を重視します。デフォルトでは、作成されるすべての変数は不変(immutable)です。つまり、letキーワードを使用して値を名前にバインドすると、その値は固定されます。後で別の値に入れ替えようとすると、コンパイラによって阻止されます。

error[E0384]: cannot assign twice to immutable variable `x`
  --> src/main.rs:4:5
   |
3  |     let x = 5;
   |         - xへの最初の代入
4  |     x = 6;
   |     ^^^^^ 不変変数に2回代入することはできません

この厳格さはセーフティネットだと考えてください。これにより、意図せずデータが変更されてしまうことで発生する一連のバグを防ぐことができます。最初は制限があるように感じるかもしれませんが、Rustにはこれを回避するための慣用的な方法がいくつか用意されています。

根本的な原因

コンパイラがE0384を出すのは、定数であると約束した値を変更しようとしているからです。Rustでは不変性が基本です。変数が変更されることをコンパイラに明示的に伝えない限り、そのスコープ内では値が一定であると見なされます。これにより、コンパイラはより積極的にコードを最適化でき、ロジックも把握しやすくなります。

解決策1:mutキーワードを使用する

最も手っ取り早い修正方法は、変数を可変(mutable)として宣言することです。mutを追加することで、コンパイラと他の開発者の両方に対して、この値が変化するものであることを示せます。

例:カウンターの追跡

fn main() {
    // 再代入を許可するために 'mut' を使用する
    let mut retry_count = 0;
    println!("現在の試行回数: {}", retry_count);

    retry_count = 1;
    println!("更新後の試行回数: {}", retry_count);
}

いつ使うべきか: 時間の経過とともに自然に変化する単一のデータがある場合は、mutを選択してください。一般的な例としては、ループカウンター、アキュムレータ、新しいデータチャンクを受け取るバッファなどが挙げられます。

解決策2:変数のシャドーイング

変数を可変にする必要はなく、単に変換したいだけの場合もあります。シャドーイングを使用すると、letキーワードを使用して新しいバインディングを作成することで、変数名を再利用できます。これにより、古い変数が「隠され(シャドーイング)」、新しい変数が優先されるようになります。

例:データの変換

fn main() {
    let input = "   42   ";
    
    // シャドーイングにより、型を &str から usize に変更できる
    let input = input.trim();
    let input: usize = input.parse().expect("数値ではありません!");

    println!("パースされた数値は: {}", input); // 42
}

シャドーイングが強力な理由:

  • 記述的な名前を維持したまま、データ型を変更できる。
  • 最終的な変換後、変数は不変のまま維持される。これにより、関数内の後半で誤って変更されるのを防げる。

解決策3:代入に式を使用する

初心者が陥りやすい罠は、if/elseブロック内で変数を初期化しようとすることです。Rustのifブロックは「式(expression)」であるため、変数を宣言してから代入する必要はありません。ブロックから直接値を返すことができます。

誤った方法:

let status_code;
if success {
    status_code = 200;
} else {
    status_code = 404;
}
// ここで status_code を変更しようとすると、E0384が発生する

Rustらしい方法:

let is_authenticated = true;

// if式全体の結果を変数にバインドする
let status_code = if is_authenticated {
    200
} else {
    401
};

println!("レスポンス: {}", status_code);

このパターンにより、status_codeは一度だけ代入されることが保証されます。これは、コードをクリーンで関数的な状態に保ちながら、コンパイラの不変ルールを満たします。

検証とテスト

修正を適用したら、次の手順で確認してください。

  • ターミナルで cargo check を実行します。これにより、フルビルドの負荷をかけずに構文と型が検証され、大規模なプロジェクトでは5〜10秒の時間を節約できます。
  • チェックが通ったら、cargo run を実行してロジックが意図通りに動作することを確認します。
  • "unused mut"(未使用のmut)警告に注意してください。Rustが「変数を可変にする必要はない」と判断した場合は、mutキーワードを削除してコードの安全性を保ちましょう。

ベストプラクティス

E0384エラーを未然に防ぐために、以下の習慣を心がけましょう。

  • デフォルトで不変にする: すべての変数は let で始めます。コンパイラやロジックが明示的に要求する場合のみ mut を追加します。
  • MatchとIfを活用する: 値の計算には matchif 式を使用します。これは、変数を宣言してから分岐内で更新するよりも、ほとんどの場合クリーンです。
  • スコープを小さく保つ: 変数を可変にする必要がある場合は、そのロジックを小さなブロック {} やヘルパー関数にラップします。これにより、値が変更される範囲が制限され、デバッグが非常に容易になります。

Related Error Notes