blog.dopana

Back

Rustには、非常に強力な制御フロー構造である match が用意されています。値を一連のパターンと比較し、最初に一致したパターンに基づいて対応するコードを実行します。

10歳でもわかる説明(ELI5):コイン選別機#

match は硬貨を選別する機械のようなものです:

  1. 上部のスロットから硬貨を投入します。
  2. 硬貨は異なる大きさの穴が空いたレールを転がり落ちます。
  3. サイズがぴったり合う最初の穴を見つけると、そのケースの中に落ちます。
  4. それぞれの硬貨は、落ちたケースに紐づくアクションのみを実行します。
graph TD
    Input["入力値 (Input Value)"] --> Arm1{"パターン1に一致?"}
    Arm1 -- はい --> Action1["アーム1を実行して結果を返す"]
    Arm1 -- いいえ --> Arm2{"パターン2に一致?"}
    Arm2 -- はい --> Action2["アーム2を実行して結果を返す"]
    Arm2 -- いいえ --> ArmCatch{"包括パターン (_ / other) に一致?"}
    ArmCatch -- はい --> ActionCatch["デフォルトのアクションを実行"]

Rustでは、コンパイラがさらに安全性を保証します。あり得るすべてのパターンを網羅(Exhaustiveness) していなければコンパイルエラーになるため、予期せぬ入力によるクラッシュを防止できます。

match の基本構文#

match 式は複数の マッチアーム(match arms) で構成されます。アームは「マッチさせるパターン」と「実行するコード」の2つの部分から成り、=> 演算子で区切られます。

[!NOTE] match は式(expression)であるため、値を返します。すべてのアームが返す値の型は同一でなければなりません。

値に束縛されるパターン (Patterns that Bind to Values)#

マッチアームの大きな特徴の1つは、Enumのバリアントに格納された内部データを取り出して変数に束縛(デストラクト)できる点です:

Coin::Quarter(UsState::Alaska) が渡されると、変数 stateUsState::Alaska が自動的に束縛されます。

Option<T> とのマッチング#

Rustには null が存在せず、代わりに Option<T>Some(T) または None)が用いられます。match を使うことで、Option から安全に値を取り出して処理できます:

src/main.rs
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

    println!("five + 1 = {six:?}");
    println!("none + 1 = {none:?}");
}
rust

マッチの網羅性と包括パターン (Catch-all)#

Rustのマッチは網羅的である必要があります。バリアントの処理を1つでも忘れるとコンパイルエラーになります:

src/main.rs
// コンパイルエラー: `None` パターンがカバーされていません!
fn plus_one_invalid(x: Option<i32>) -> Option<i32> {
    match x {
        Some(i) => Some(i + 1),
    }
}
rust

包括変数とプレースホルダー _#

特定のパターンだけを処理し、残りのケースすべてにデフォルトの処理を適用したい場合:

[!TIP] 包括パターン(other_)は常に match の最後に配置する必要があります(上から順に評価されるため)。

まとめ#

  • match は値をパターンと順次比較し、最初に一致したアームを実行します。
  • Enumのバリアント内に含まれるデータを取り出して変数に束縛できます。
  • コンパイラが全ケースの網羅を強制するため、漏れのない安全なコードが書けます。
  • 残りのケースをまとめて扱うときは、末尾に _ または包括変数を指定します。

参考文献#