Rustを学び始めた方が最初に驚くことの一つに、「セミコロンの有無でプログラムの挙動が劇的に変わる」という点があります。

他の多くのC系言語において、セミコロンは単なる「行の終わり」を告げる記号に過ぎません。

しかし、Rustという言語においては、その一文字が「値を返すのか、それとも何も返さないのか」を決定づける極めて重要な役割を担っています。

Rustは「式ベース(Expression-based)」の言語として設計されています。

この設計思想を正しく理解することは、所有権システムや型システムを理解することと同様に、Rustマスターへの避けては通れない道です。

本記事では、2026年現在の最新のプラクティスを交えながら、Rustにおける「式」と「文」の決定的な違いを詳しく解き明かしていきます。

Rustにおける「式」と「文」の根本的な違い

Rustのプログラムは、主に「文 (Statement)」と「式 (Expression)」の2種類で構成されています。

これらは似ているようで、その性質は根本的に異なります。

まず、最も簡潔にその違いを定義するならば、以下のようになります。

  • 文 (Statement):何らかのアクションを実行し、値を返さない命令。
  • 式 (Expression):評価(計算)され、その結果として「値」を生成するもの。

Rustの関数の本体は、通常は一連の文で構成され、最後に任意で式を置くという形を取ります。

この「最後に式を置く」という点が、Rust特有の簡潔な記述を可能にしています。

特徴文 (Statement)式 (Expression)
値の返却なし(厳密にはユニット型 ()あり
セミコロン必須通常は付けない(付けると文に変わる)
変数宣言 (let)数値、演算、関数呼び出し、ブロック

この違いを意識せずにコードを書くと、コンパイルエラーに直面したり、意図しない型不一致が発生したりすることになります。

「文 (Statement)」の役割と種類

Rustにおける「文」は、プログラムの状態を変化させたり、型を宣言したりするために使用されます。

文は値を生成しないため、別の変数の値として代入することはできません。

宣言文 (Declaration Statement)

最も代表的な文は、変数宣言を行う letです。

Rust
fn main() {
    // これは「文」です
    let x = 5; 
    
    // 次のような記述はエラーになります。let文は値を返さないからです。
    // let y = (let x = 5); 
}

上記のコードで、let x = 5; は全体として「文」です。

一方で、右辺にある 5 自体は「式」です。

Rustでは、式を文の中に含めることで、プログラムを進行させていきます。

式文 (Expression Statement)

式文とは、式の末尾にセミコロンを付けることで、その式を文に変えたものを指します。

Rust
fn main() {
    // 本来は式である 5 + 5 の末尾にセミコロンを付ける
    5 + 5; 
}

通常、5 + 510 という値を生成する式ですが、セミコロンを付けることで「値を捨てて、何もしない文」へと変化します。

このとき、この文が返す値は「ユニット型」と呼ばれる () になります。

ユニット型は、C言語などでいうところの void に近い概念ですが、Rustでは明確に「値を持たない型」として扱われます。

「式 (Expression)」の多様な姿

Rustが「式ベースの言語」と呼ばれる理由は、他の言語では「文」として扱われる制御構造の多くが、Rustでは「式」として定義されているからです。

制御フローとしての式 (if, match)

例えば、if 構造はRustでは式です。

つまり、if文の結果を変数に代入することができます

Rust
fn main() {
    let condition = true;

    // if式の結果を変数 number に代入
    let number = if condition {
        5
    } else {
        6
    };

    println!("値の結果は: {}", number);
}
実行結果
値の結果は: 5

この例では、if ブロックの最後の行にセミコロンがありません。

これにより、それぞれのブロックが値を返す「式」として機能しています。

もしここでセミコロンを付けてしまうと、そのブロックは値を返さなくなり、型不一致のエラーが発生します。

また、match も同様に式です。

2026年現在のRust開発においても、複雑な条件分岐から値を取り出す際には、この「式としての性質」をフル活用するのが一般的です。

ブロック式と戻り値

波括弧 {} で囲まれたスコープも、実は「式」として扱うことができます。

これを「ブロック式」と呼びます。

Rust
fn main() {
    let y = {
        let x = 3;
        x + 1 // ここにセミコロンがないことに注目
    };

    println!("yの値は: {}", y);
}
実行結果
yの値は: 4

ブロック内の最後の行にセミコロンがない場合、その評価結果がブロック全体の戻り値となります。

一方、let x = 3; は文であるため、値を返しません。

このように、スコープの最終行を式にすることで、一時的な変数計算をカプセル化して値を抽出できるのがRustの強みです。

セミコロンの有無がコードに与える影響

Rustプログラマが最も気を遣うのが、関数の末尾やブロックの末尾におけるセミコロンの有無です。

これによって、関数の戻り値の型が決定されるからです。

セミコロンを忘れた場合・付けた場合の違い

以下の2つの関数を比較してみましょう。

Rust
// 正しい例:数値を返す
fn add_one(x: i32) -> i32 {
    x + 1
}

// 誤った例:コンパイルエラーになる
/*
fn add_one_error(x: i32) -> i32 {
    x + 1; // セミコロンがあるため、戻り値が () になる
}
*/

add_one_error 関数では、x + 1; とセミコロンを付けてしまったため、この行は「式」ではなく「文」になってしまいました。

文が返す値は () ですが、関数のシグネチャでは i32 を返すよう定義されています。

この「型不一致」により、コンパイラはエラーを報告します。

関数末尾の暗黙的なリターン

Rustでは、関数の最後の式がその関数の戻り値になります。

明示的に return キーワードを使うことも可能ですが、関数の最後の行では 「セミコロンを省いた式」を置くのがRustらしい、慣用的な書き方とされています。

もちろん、関数の途中で早期リターン(Early Return)をしたい場合には return 文を使用します。

Rust
fn check_value(x: i32) -> String {
    if x < 0 {
        return String::from("負の数です"); // 早期リターンは明示的に
    }

    String::from("正の数です") // 最後の式はセミコロンなし
}

実践的な使い分けとテクニック

式と文の性質を理解すると、より高度なRustの機能を使いこなせるようになります。

let文との組み合わせ

Rustの強力な型推論は、式から導き出される型を正確に追跡します。

前述したブロック式を let 文と組み合わせる手法は、変数の有効範囲を最小限に抑えるために非常に有効です。

Rust
fn main() {
    let result = {
        let a = 10;
        let b = 20;
        a * b
    };
    // ここでは a や b にアクセスできないため、名前空間がクリーンに保たれる
    println!("計算結果: {}", result);
}

エラーハンドリングと式

2026年現在、Rustのエラーハンドリングにおいて ? 演算子は欠かせない存在です。

この ? 演算子も、実は式の一部として機能します。

Rust
fn get_data() -> Result<i32, String> {
    let value = maybe_fail()? + 10; // maybe_fail()? は式
    Ok(value)
}

fn maybe_fail() -> Result<i32, String> {
    Ok(5)
}

このように、演算の途中に「式」としてエラーチェックを組み込めるのも、Rustが徹底して式ベースの構造を持っている恩恵です。

2026年現在のRustにおけるベストプラクティス

Rustのエディション(Edition)が重なるにつれ、言語の安定性と一貫性はさらに高まっています。

2024エディション以降の現代的なRust開発においても、式と文の扱いは以下のルールに従うのがベストです。

  1. 短い関数やクロージャでは、可能な限り式を戻り値にする。 不要な return は避け、コードのノイズを減らしましょう。
  2. 副作用(画面出力やファイルの書き込みなど)を目的とする場合は、明示的にセミコロンを付けて文にする。 これにより、読者は「この行は値を生成するためのものではない」と一目で理解できます。
  3. ユニット型 () を意識する。 関数が値を返さない場合、それは技術的には「ユニット型という値を返している」のだという認識を持つことで、トレイトの境界条件やジェネリクスの理解が深まります。

また、最新のLinter(Clippyなど)は、セミコロンの付け忘れや不要なセミコロンに対して非常に的確な指摘をしてくれます。

開発環境の警告を無視せず、式のセマンティクスを意識したコーディングを心がけましょう。

まとめ

Rustにおける「式」と「文」の違いは、単なる文法上のルールに留まりません。

それは、「すべての操作に型があり、多くの操作が値を生成する」というRustの哲学を体現したものです。

  • は、let による宣言や、セミコロンで終わる命令であり、状態を変化させますが値は返しません。
  • は、評価されて値を生成し、関数の戻り値や変数の初期化に利用されます。
  • セミコロン一つで、その行が「価値のある情報を生むのか」それとも「単なる手続きとして終わるのか」が決まります。

この仕組みを理解することで、Rustのコンパイルエラーは「謎の呪文」から「論理的なアドバイス」へと変わるはずです。

セミコロンを打つその瞬間、そのコードが「式」として値を紡いでいるのか、それとも「文」として役割を終えているのかを、ぜひ一度意識してみてください。

その小さな意識の積み重ねが、堅牢で美しいRustコードを書くための第一歩となるでしょう。