末尾にセミコロンを付けると戻り値が () になり E0308 になる
Rustの関数は最後の行が式(セミコロン無し)であれば、その値をそのまま戻り値にします。セミコロンを付けるとその行は文(statement)になり、戻り値が空のタプル()に変わってしまいます。
エラーメッセージの読み方
error[E0308]: mismatched types
E0308- エラーコード — `rustc --explain E0308` で詳しい説明が見られます
mismatched types- 詳細メッセージ — 何が問題だったか
このエラーが出る典型パターン
パターン1
1 fn double(n: i32) -> i32 { 2 n * 2; ^ 3 } 4 5 fn main() { 6 println!("{}", double(3)); 7 }
関数の最後の行はセミコロンを付けなければ式として評価され、その値が戻り値になります。セミコロンを付けるとその行は文になり、戻り値は()になります。
直し方: ; を (空) にします。
パターン2
1 fn triple(n: i32) -> i32 { 2 n * 3; ^ 3 } 4 5 fn main() { 6 println!("{}", triple(4)); 7 }
戻り値の型をi32と宣言しているのに、末尾にセミコロンを付けると()を返すことになり型が一致しなくなります。
直し方: ; を (空) にします。
パターン3
1 fn square(n: i32) -> i32 { 2 n * n; ^ 3 } 4 5 fn main() { 6 println!("{}", square(5)); 7 }
returnを書かない場合、関数本体の最後の式(セミコロン無し)がそのまま戻り値になります。
直し方: ; を (空) にします。
パターン4
1 fn increment(n: i32) -> i32 { 2 n + 1; ^ 3 } 4 5 fn main() { 6 println!("{}", increment(9)); 7 }
他の言語のようにセミコロンを付けるだけの習慣で書くと、Rustでは戻り値の型が変わってしまいます。
直し方: ; を (空) にします。
パターン5
1 fn half(n: i32) -> i32 { 2 n / 2; ^ 3 } 4 5 fn main() { 6 println!("{}", half(10)); 7 }
セミコロンの有無は単なる区切り記号ではなく、その行を式として扱うか文として扱うかを決める重要な違いです。
直し方: ; を (空) にします。
よくある誤解
「セミコロンは行の区切りとして付けても付けなくても同じはず」という思い込みは誤りです。関数の最後の行では、セミコロンの有無が戻り値の型そのものを変えてしまいます。
実務での勘所
Rustのこの挙動は、ML系・関数型寄りの言語から受け継いだ「あらゆるものは値を持つ式である」という設計に基づいています。C系の言語のvoid(何も返さないという特別な無)とは違い、Rustの()(ユニット型)は「取りうる値がただ1つしかない、れっきとした型」です。セミコロンを付けるとその行は「値を捨てて次に進む文」になり、関数の最後でこれをやってしまうと、戻り値は暗黙的にこのユニット型の値になります。「何も返さない」ではなく「()という決まった値を返している」という理解が正確です。