if letで簡潔なフロー制御

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


# #![allow(unused_variables)]
#fn main() {
let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
#}

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

Some(3)にマッチした時だけ何かをし、他のSome<u8>値やNone値の時には何もしたくありません。 match式を満たすためには、列挙子を一つだけ処理した後に_ => ()を追加しなければなりません。 これでは、追加すべき定型コードが多すぎます。

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


# #![allow(unused_variables)]
#fn main() {
# let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
    println!("three");
}
#}

if letという記法は等号記号で区切られたパターンと式を取り、式がmatchに与えられ、パターンが最初のアームになったmatchと、 同じ動作をします。

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

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

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


# #![allow(unused_variables)]
#fn main() {
# #[derive(Debug)]
# enum UsState {
#    Alabama,
#    Alaska,
# }
#
# enum Coin {
#    Penny,
#    Nickel,
#    Dime,
#    Quarter(UsState),
# }
# let coin = Coin::Penny;
let mut count = 0;
match coin {
    // {:?}州のクォーターコイン
    Coin::Quarter(state) => println!("State quarter from {:?}!", state),
    _ => count += 1,
}
#}

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


# #![allow(unused_variables)]
#fn main() {
# #[derive(Debug)]
# enum UsState {
#    Alabama,
#    Alaska,
# }
#
# enum Coin {
#    Penny,
#    Nickel,
#    Dime,
#    Quarter(UsState),
# }
# 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のモジュールに目を向けてみましょう。