Rustを学び始めた方が最初に驚く文法規則の一つに、行末のセミコロンの有無によってプログラムの意味が劇的に変わるという点があります。
多くのプログラミング言語において、セミコロンは単なる「文の終端」を示す記号に過ぎませんが、Rustにおいては「値を返すか、それとも捨てるか」を決定づける極めて重要な役割を担っています。
この仕様を正しく理解することは、Rust特有のコンパイルエラーを解消し、簡潔で安全なコードを書くための第一歩です。
本記事では、Rustにおけるセミコロンの省略が持つ本来の意味から、式と文の厳密な違い、そして実務で役立つ具体的なコード例までを詳しく掘り下げていきます。
Rustにおけるセミコロンの基本的な役割
Rustのプログラムは、主に「文 (Statement)」と「式 (Expression)」の組み合わせで構成されています。
他の言語との最大の違いは、Rustは「式ベース」の言語であるという点です。
多くの場合、関数のブロックや制御フローの中で、最後の行にセミコロンを付けないことで、その行の評価値を「戻り値」として扱うことができます。
逆に、セミコロンを付けることは、その式の評価値を破棄し、その行を「文」に変えることを意味します。
文 (Statement) とは何か
文は、何らかの動作を実行しますが、値を返さない命令です。
Rustにおける代表的な文には、変数定義を行う let 文があります。
fn main() {
// これは「宣言文」です。値を返さないため、変数に代入することはできません。
let x = 5;
// 次のような記述はコンパイルエラーになります。
// let y = (let z = 10);
}
文の末尾には必ずセミコロンが必要です。
セミコロンを付けることで、コンパイラに対して「この命令はここで完結しており、これ以降に値を引き継がない」ことを明示します。
式 (Expression) とは何か
対して式は、値を評価し、その結果を呼び出し元に返すものです。
Rustでは、ほとんどの構文が式として扱われます。
- 数値リテラル (例:
5) - 算術演算 (例:
5 + 10) - 関数の呼び出し
- ブロックスコープ
{ ... }
これらはすべて値を生成するため、セミコロンを省略することで、その値をブロックの外へ渡すことが可能になります。
「式」を「文」に変えるセミコロンの魔法
Rustにおいて、式にセミコロンを付け足すと、その式は「式文 (Expression Statement)」へと変化します。
式文の役割は、式の評価結果を捨てて、型をユニット型 () に変換することにあります。
ユニット型 () とは
Rustには、何も値がないことを表す () (ユニット型) という特殊な型が存在します。
他の言語における void に似ていますが、Rustではこれも一つの値として扱われます。
式にセミコロンを付けると、その式の本来の戻り値は無視され、結果としてユニット型が返されます。
fn example() -> i32 {
let x = 10;
x + 5 // セミコロンがないので、i32型の「15」が返される
}
fn example_with_semi() {
let x = 10;
x + 5; // セミコロンがあるため、15は捨てられ、戻り値は () になる
}
上記の例でわかるように、セミコロン一つで関数の戻り値の型が一致しなくなり、コンパイルエラーの原因となることが多々あります。
セミコロン省略による戻り値の扱い
Rustの関数において、最後に評価された式の値が自動的に関数の戻り値になるというルールがあります。
これにより、明示的な return キーワードを省略した、クリーンなコードを記述できます。
暗黙の戻り値を利用したコード
以下のコードは、2つの整数の和を返す関数です。
fn add(a: i32, b: i32) -> i32 {
// return a + b; と書くのと同じ意味ですが、Rustではこちらが推奨されます
a + b
}
fn main() {
let result = add(10, 20);
println!("結果は: {}", result);
}
結果は: 30
ここで、a + b の後ろにセミコロンを付けてしまうと、この関数は i32 ではなく () を返そうとするため、型ミスマッチでエラーになります。
明示的な return との使い分け
もちろん、関数の途中で早期リターンをしたい場合には、return キーワードが必要です。
fn check_number(n: i32) -> String {
if n < 0 {
// 途中で抜ける場合は return が必須
return String::from("負の数です");
}
// 関数の最後なのでセミコロンを省略して値を返す
String::from("正の数またはゼロです")
}
「関数の最後はセミコロンを省く」のがRustのイディオムであり、これに従うことで「この値がこの関数の最終的な成果物である」という意図を読み手に伝えることができます。
制御構造におけるセミコロンの挙動
Rustでは、if や match、さらには { } で囲まれたブロック自体も式です。
これらの中でもセミコロンの有無は重要な役割を果たします。
if式での活用
if が式であるため、条件分岐の結果を直接変数に代入できます。
fn main() {
let condition = true;
// ifブロック全体が式として評価される
let number = if condition {
5 // セミコロンなし:5を返す
} else {
10 // セミコロンなし:10を返す
};
println!("値は: {}", number);
}
値は: 5
もしここで 5; と書いてしまうと、number に代入される値はユニット型になってしまい、後続の処理で数値として扱おうとした際にエラーが発生します。
ブロック式 { } の評価
通常のプログラム中で、一時的な計算のためにスコープを作りたい場合も、ブロックの最後にセミコロンを置かないことで、その計算結果を取り出すことができます。
fn main() {
let x = {
let y = 3;
let z = 2;
y * z // この値がブロックの評価値になる
};
println!("xの値は: {}", x);
}
xの値は: 6
このように、ブロック内の最終行にセミコロンがない場合、そのブロックはその式の値を持つというルールは、Rustのあらゆる場所で一貫して適用されています。
よくあるエラーと対処法:なぜコンパイルが通らないのか?
セミコロンの有無に関するエラーは、Rust初心者が最も遭遇しやすい壁の一つです。
いくつかの代表的なパターンを見てみましょう。
1. 戻り値の型ミスマッチ (mismatched types)
最も多いのが、値を返すべき関数で誤ってセミコロンを付けてしまうケースです。
fn square(n: i32) -> i32 {
n * n; // エラー!セミコロンがあるため () を返している
}
このとき、コンパイラは次のような親切なメッセージを出してくれます。
help: consider removing this semicolon (このセミコロンを削除することを検討してください)
2. ユニット型を期待している場所に値がある
逆に、値を返してはいけない場所でセミコロンを忘れるケースもあります。
fn print_total(a: i32, b: i32) { // 戻り値は ()
let sum = a + b;
sum // エラー!戻り値が i32 になっているが、シグネチャは () を期待
}
この場合、sum の後ろにセミコロンを付けるか、関数の戻り値型を適切に設定する必要があります。
3. if式の各アームの型不一致
if 式の true の場合と false の場合で、セミコロンの有無が混在するとエラーになります。
let val = if true {
100
} else {
200; // エラー:一方は i32、もう一方は () を返している
};
すべての分岐(アーム)は、同じ型の値を返さなければなりません。
セミコロンの有無による挙動の比較まとめ
ここまでの内容を整理すると、以下の表のようになります。
| 項目 | セミコロンあり (;) | セミコロンなし |
|---|---|---|
| 分類 | 文 (Statement) | 式 (Expression) |
| 評価値 | なし (正確には ()) | 式が計算された結果の値 |
| 主な用途 | 変数定義、副作用の実行、処理の区切り | 関数の戻り値、ブロックの評価値の決定 |
| コンパイラの解釈 | 「この処理を実行して、値を捨てろ」 | 「この処理の結果を次に渡せ」 |
Rust開発におけるベストプラクティス
2026年現在のRust開発においても、セミコロンの扱いはコードの可読性に直結する重要な要素です。
以下のガイドラインを意識することで、より「Rustらしい」コードを書くことができます。
関数の末尾では return を使わずセミコロンを省略する
Rustのコミュニティでは、関数の最後の式はセミコロンを省略することが標準的なスタイルです。これにより、関数の目的が明確になります。副作用目的の式には必ずセミコロンを付ける
println!()のようなマクロや、状態を変更するメソッドを呼び出す際は、たとえそれが関数の最後であっても、セミコロンを付けて「この行は値を返すためのものではない(副作用のための文である)」ことを明示するのが一般的です。ただし、関数の戻り値が()であれば省略しても動作は変わりません。複雑なロジックはブロック式でカプセル化する
変数を初期化する際に、複雑な計算が必要な場合は、即時実行されるブロック式{ ... }を使い、その中で一時的な変数を用いながら、最後にセミコロンなしで値を返す手法が非常に有効です。これにより、不必要なミュータブル(可変)変数を減らすことができます。
まとめ
Rustにおけるセミコロンは、単なる改行の代わりではなく、「データの流れ」を制御するための重要なスイッチです。
- セミコロンを省略すれば「式」となり、値が外へ流れていく。
- セミコロンを付ければ「文」となり、値はその場で消費(破棄)される。
このシンプルな原則を理解するだけで、Rustの関数設計やエラーメッセージの解読が驚くほどスムーズになります。
式ベースの言語であるRustの特性を活かし、セミコロンを意識的に使い分けることで、安全で洗練されたプログラムを構築していきましょう。
もしコンパイルエラーに遭遇したときは、まずはその行が「値を返すべき場所なのか、そうでないのか」を確認してみてください。
多くの場合、解決のヒントはたった一つのセミコロンに隠されています。
