コーディングの障壁深夜の作業中にロジックを整理していて、簡単な条件分岐を使って値を代入しようとしたとします。簡潔な1行で済むはずが、Rustコンパイラは次のような鋭いエラーメッセージであなたの手を止めます。
error[E0317]: if may be missing an else clause
--> src/main.rs:5:13
|
5 | let x = if condition {
| ____________^
6 | | 10
7 | | };
| |_____^ expected (), found i32
|
= note: `if` expressions without `else` evaluate to `()`
= help: add an `else` block that evaluates to the expected type
PythonやJavaScriptのような言語では、変数を未定義のままにしてスルーされるかもしれませんが、Rustは実行時のクラッシュを防ぐために明示性を求めます。
エラーの原因Rustでは、ifを単なる文ではなく式として扱います。つまり、値を返さなければなりません。let x = if ...のように、ifを使って変数に結果を代入する場合、コンパイラはあらゆる可能性においてxに有効な値が渡されることを保証しようとします。
elseブランチを省略すると、Rustは暗黙的にユニット型である()を返します。これにより、即座に型の不一致が発生します。ifブロックが32ビット整数(i32)を返しているのに、隠れたelseが何も返さない場合、コンパイラは変数に対して単一で一貫した型を割り当てることができません。
ステップ別の修正方法#### 1. フォールバック用のelse句を追加するデフォルト値を提供することが最も直接的な解決策です。すべての実行パスが同じデータ型を返す必要があります。
// ❌ E0317を発生させる
let is_active = true;
let speed = if is_active {
100
}; // エラー: () が期待されていますが、i32 が見つかりました
// ✅ フォールバックを追加して修正
let speed = if is_active {
100
} else {
0
};
2. デフォルト値で初期化するelseブロックが冗長に感じられることもあります。その場合は、まず可変変数を初期化します。これにより、ifは代入式ではなく、単純な制御フロー文になります。
let mut retry_count = 0; // 標準のデフォルト値
if failure_detected {
retry_count = 5;
} // ifブロックを使って変数の型を定義していないため、エラーは発生しません
3. 複雑なロジックにはmatchを使用する3つ以上の条件を扱う場合は、matchブロックの方が圧倒的に読みやすくなります。すべてのケースを網羅することを強制されるため、if/elseと同じ安全性が得られます。
let connection_state = "loading";
let code = match connection_state {
"active" => 200,
"loading" => 102,
_ => 500, // すべてに一致する「else」と同等のもの
};
特殊なケース:Optionを使用する論理的なデフォルト値が存在しない場合は、結果をOptionでラップします。これにより、値が欠落している可能性があることをプログラムの他の部分に明示的に伝えることができます。
let user_id = if user_found {
Some(1024)
} else {
None
};
修正の確認コンパイラのチェックを実行して、エラーが解消されたことを確認します。
cargo check
修正が成功すればE0317は消えます。さらに、フォールバック値が期待通りに動作することを確認するために、簡単なユニットテストを追加してロジックを検証することもできます。
#[test]
fn test_speed_default() {
let is_active = false;
let val = if is_active { 100 } else { 0 };
assert_eq!(val, 0);
}

