if let

列挙型をマッチさせるとき、場合によってはmatchを使用すると不自然な書き方になってしまう場合があります。例えば

#![allow(unused)]
fn main() {
// `optional`という変数の型を`Option<i32>`に指定。
let optional = Some(7);

match optional {
    Some(i) => println!("This is a really long string and `{:?}`", i),
    _ => {},
    // ^ `match`は全てのパターンに対して網羅的でなくてはならないので必要。
    // 冗長に見えませんか?
};

}

この場合はif letを用いたほうが美しく、失敗時の処理も柔軟に行うことができます。

fn main() {
    // 全て`Option<i32>`型
    let number = Some(7);
    let letter: Option<i32> = None;
    let emoticon: Option<i32> = None;

    // `if let`文は以下と同じ意味。
    // もしletがnumberをデストラクトした結果が`Some(i)`になるならば
    // ブロック内(`{}`)を実行します。
    if let Some(i) = number {
        println!("Matched {:?}!", i);
    }

    // デストラクトした結果が`Some()`にならない場合の処理を明示したい場合、
    // `else`を使用します。
    if let Some(i) = letter {
        println!("Matched {:?}!", i);
    } else {
        // デストラクト失敗の場合。このブロック内を実行。
        println!("Didn't match a number. Let's go with a letter!");
    }

    // デストラクト失敗時の処理を更に分岐させることもできます。
    let i_like_letters = false;

    if let Some(i) = emoticon {
        println!("Matched {:?}!", i);
    // デストラクト失敗。`else if`を評価し、処理をさらに分岐させます。
    } else if i_like_letters {
        println!("Didn't match a number. Let's go with a letter!");
    } else {
        // 今回は`else if`の評価がfalseなので、このブロック内がデフォルト。
        println!("I don't like letters. Let's go with an emoticon :)!");
    }
}

同様にif letは任意の列挙型のマッチに使えます。

// 列挙型の例
enum Foo {
    Bar,
    Baz,
    Qux(u32)
}

fn main() {
    // 変数の作成
    let a = Foo::Bar;
    let b = Foo::Baz;
    let c = Foo::Qux(100);
    
    // Foo::Barにマッチする変数
    if let Foo::Bar = a {
        println!("a is foobar");
    }
    
    // 変数bはFoo::Barにマッチしないので出力されません。
    if let Foo::Bar = b {
        println!("b is foobar");
    }
    
    // 変数cはFoo::Quxにマッチしつつ値を取り出せます。
    // これはSome()と同様です。
    if let Foo::Qux(value) = c {
        println!("c is {}", value);
    }

    // 束縛も可能です。
    if let Foo::Qux(value @ 100) = c {
        println!("c is one hundred");
    }
}

もう一つのメリットはif letがパラメータを持たない列挙型にも使えることです。列挙型がPartialEqを実装または導出していなくても問題ありません。その場合、列挙型のインスタンスは比較できないのでif Foo::Bar == aはコンパイルエラーとなりますが、if letは引き続き使えます。

試してみましょう。以下の例をif letを使って直してみてください。

// この列挙型はPartialEqを実装も導出もしていません。
// そのためFoo::Bar == aはエラーとなります。
enum Foo {Bar}

fn main() {
    let a = Foo::Bar;

    // Foo::Barにマッチする変数
    if Foo::Bar == a {
    // ^-- ここでコンパイルエラー。`if let`を使ってみましょう。
        println!("a is foobar");
    }
}

参照

列挙型, Option, RFC