マッチ

しばしば、2つ以上の可能な処理が存在するためや、分岐条件が非常に複雑になるために単純な if/else では充分でない場合があります。 Rustにはキーワード match が存在し、複雑な if/else のグループをさらに強力なもので置き換えられます。 以下の例を見てみましょう:

fn main() { let x = 5; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), 4 => println!("four"), 5 => println!("five"), _ => println!("something else"), } }
let x = 5;

match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    _ => println!("something else"),
}

match は一つの式とその式の値に基づく複数のブランチを取ります。 一つ一つの「腕」は val => expression という形式を取ります。 値がマッチした時に、対応する腕の式が評価されます。 このような式が match と呼ばれるのは「パターンマッチ」という用語に由来します。 パターン のセクションではこの部分に書けるすべてのパターンを説明しています。

match を使う利点は何でしょうか? いくつか有りますが、 まず一つ目としては match をつかうことで、「網羅性検査」が実施されます。 上のコードで、最後のアンダースコア( _ )を用いている腕があるのがわかりますか? もし、その腕を削除した場合、Rustは以下の様なエラーを発生させます:

error: non-exhaustive patterns: `_` not covered

言い換えると、Rustは値を忘れていることを伝えようとしているのです。 なぜなら x は整数であるため、Rustは x は多くの異なる値を取ることができることを知っています。 例えば、 6 などがそれに当たります。 もし _ がなかった場合、 6 にマッチする腕が存在しない事になります、そのためRustはコンパイルを通しません。 _ は「全てキャッチする腕」のように振る舞います。 もし他の腕がどれもマッチしなかった場合、 _ の腕が実行されることになります、 この「全てキャッチする腕」が存在するため、 x が取り得るすべての値について対応する腕が存在することになり、コンパイルが成功します。

match は式でもあります、これはつまり let 束縛の右側や式が使われているところで利用することができるということを意味しています。

fn main() { let x = 5; let number = match x { 1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five", _ => "something else", }; }
let x = 5;

let number = match x {
    1 => "one",
    2 => "two",
    3 => "three",
    4 => "four",
    5 => "five",
    _ => "something else",
};

このようにして、ある型から他の型への変換がうまく書ける場合があります。

列挙型に対するマッチ

match の他の重要な利用方法としては列挙型のバリアントを処理することがあります:

fn main() { enum Message { Quit, ChangeColor(i32, i32, i32), Move { x: i32, y: i32 }, Write(String), } fn quit() { /* ... */ } fn change_color(r: i32, g: i32, b: i32) { /* ... */ } fn move_cursor(x: i32, y: i32) { /* ... */ } fn process_message(msg: Message) { match msg { Message::Quit => quit(), Message::ChangeColor(r, g, b) => change_color(r, g, b), Message::Move { x: x, y: y } => move_cursor(x, y), Message::Write(s) => println!("{}", s), }; } }
enum Message {
    Quit,
    ChangeColor(i32, i32, i32),
    Move { x: i32, y: i32 },
    Write(String),
}

fn quit() { /* ... */ }
fn change_color(r: i32, g: i32, b: i32) { /* ... */ }
fn move_cursor(x: i32, y: i32) { /* ... */ }

fn process_message(msg: Message) {
    match msg {
        Message::Quit => quit(),
        Message::ChangeColor(r, g, b) => change_color(r, g, b),
        Message::Move { x: x, y: y } => move_cursor(x, y),
        Message::Write(s) => println!("{}", s),
    };
}

繰り返しになりますが、Rustコンパイラは網羅性のチェックを行い、列挙型のすべてのバリアントに対して、マッチする腕が存在することを要求します。 もし、一つでもマッチする腕のないバリアントを残している場合、 _ を用いなければコンパイルエラーが発生します。

先ほど説明した値に対する match の利用とは異なり、列挙型のバリアントに基いた分岐に if を用いることはできません。 列挙型のバリアントに基いた分岐には if let 文を用いることが可能です。 if letmatch の短縮形と捉えることができます。