if letで簡潔な制御フロー

if let記法でifletをより冗長性の少ない方法で組み合わせ、残りを無視しつつ、一つのパターンにマッチする値を扱うことができます。 config_max変数のOption<u8>値にマッチするけれど、値がSome列挙子の時にだけコードを実行したい、リスト6-6のプログラムを考えてください。

fn main() {
    let config_max = Some(3u8);
    match config_max {
        Some(max) => println!("The maximum is configured to be {}", max),
        _ => (),
    }
}

リスト6-6: 値がSomeの時だけコードを実行するmatch

値がSomeの場合は、そのSome列挙子の値をパターン内の変数maxに束縛することで、それを出力します。 None値に対しては何もしたくありません。 match式の要件を満たすためには、列挙子を一つだけ処理した後に_ => ()を追加しなければなりませんが、これは煩わしい定型コードです。

その代わり、if letを使用してもっと短く書くことができます。以下のコードは、 リスト6-6のmatchと同じように振る舞います:

fn main() {
    let config_max = Some(3u8);
    if let Some(max) = config_max {
        println!("The maximum is configured to be {}", max);
    }
}

if letという記法は等号記号で区切られたパターンと式を取り、式がmatchに与えられ、パターンが最初のアームになったmatchと同じ動作をします。 この場合は、パターンはSome(max)maxSome内の値に束縛されます。 そうすると、対応するmatchアームの中でmaxを使用したのと全く同じように、if letブロックの本体の中でmaxを使用することができます。 値がパターンにマッチしない場合は、if letブロック内のコードは実行されません。

if letを使うと、タイプ数が減り、インデントも少なくなり、定型コードも減ります。しかしながら、 matchでは強制された包括性チェックを失ってしまいます。matchif letかの選択は、 特定の場面でどんなことをしたいかと簡潔性を得ることが包括性チェックを失うのに適切な代償となるかによります。

言い換えると、if letは値が一つのパターンにマッチした時にコードを走らせ、 他は無視するmatchへの糖衣構文と考えることができます。

if letでは、elseを含むこともできます。elseに入るコードブロックは、 if letelseに等価なmatch式の_の場合に入るコードブロックと同じになります。 リスト6-4のCoin enum定義を思い出してください。ここでは、Quarter列挙子は、 UsStateの値も保持していましたね。クォーターコインの状態を告げつつ、 見かけたクォーター以外のコインの枚数を数えたいなら、以下のようにmatch式で実現することができるでしょう:

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // --略--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {
    let coin = Coin::Penny;
    let mut count = 0;
    match coin {
        Coin::Quarter(state) => println!("State quarter from {:?}!", state),
        _ => count += 1,
    }
}

または、以下のようにif letelseを使うこともできるでしょう:

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
    // --略--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn main() {
    let coin = Coin::Penny;
    let mut count = 0;
    if let Coin::Quarter(state) = coin {
        println!("State quarter from {:?}!", state);
    } else {
        count += 1;
    }
}

matchを使って表現するには冗長的すぎるロジックがプログラムにあるようなシチュエーションに遭遇したら、 if letもRust道具箱にあることを思い出してください。

まとめ

これで、enumを使用してワンセットの列挙された値のどれかになりうる独自の型を生成する方法を講義しました。 標準ライブラリのOption<T>が型システムを使用して、エラーを回避する際に役立つ方法についても示しました。 enumの値がデータを内部に含む場合、処理すべきケースの数に応じて、matchif letを使用して値を取り出し、 使用できます。

もうRustプログラムで構造体とenumを使用して、自分の領域の概念を表現できます。API内で使用するために独自の型を生成することで、 型安全性を保証することができます: コンパイラが、各関数の予期する型の値のみを関数が得ることを確かめてくれるのです。

使用するのに率直な整理整頓されたAPIをユーザに提供し、ユーザが必要とするものだけを公開するために、 今度は、Rustのモジュールに目を向けてみましょう。