値のマッチング

match キーワードを使うと、値を 1 つ以上の パターン に照合できます。パターン は C や C++ の switch と同様に単純な値にもできますが、より複雑な条件を表現する ためにも使えます:

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

#[rustfmt::skip]
fn main() {
    let input = 'x';
    match input {
        'q'                       => println!("Quitting"),
        'a' | 's' | 'w' | 'd'     => println!("Moving around"),
        '0'..='9'                 => println!("Number input"),
        key if key.is_lowercase() => println!("Lowercase: {key}"),
        _                         => println!("Something else"),
    }
}

パターン内の変数(この例では key)は、match アーム内で使用できる バインディングを作成します。これについては次のスライドでさらに学びます。

match ガードがあると、そのアームは条件が真の場合にのみマッチします。条件が 偽なら、後続のケースのチェックが続行されます。

重要なポイント:

  • パターンの中で、いくつかの特定の文字がどのように使われているかを 指摘するとよいでしょう

    • |or
    • .. は任意の個数の要素にマッチします
    • 1..=5 は終端を含む範囲を表します
    • _ はワイルドカードです
  • 独立した構文機能としての match ガードは、パターンだけでは表現しきれない より複雑な考えを簡潔に表現したいときに重要かつ必要です。

  • match ガードは、=> の後に置く if 式とは異なります。if 式は match アームが選択されたあとで評価されます。そのブロック内の if 条件が満たされなくても、元の match 式のほかのアームが検討されることはありません。 次の例では、ワイルドカードパターン _ => は試されることすらありません。

// Copyright 2024 Google LLC
// SPDX-License-Identifier: Apache-2.0

#[rustfmt::skip]
fn main() {
    let input = 'a';
    match input {
        key if key.is_uppercase() => println!("Uppercase"),
        key => if input == 'q' { println!("Quitting") },
        _   => println!("Bug: this is never printed"),
    }
}
  • ガードで定義した条件は、| を含むパターン内のすべての式に適用されます。

  • 既存の変数を match アーム内でそのまま使って条件にすることはできない点に注意して ください。代わりにそれは変数名パターンとして解釈され、既存の変数を シャドーイングする新しい変数が作られます。たとえば次のようになります:

    #![allow(unused)]
    fn main() {
    // Copyright 2024 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    let expected = 5;
    match 123 {
        expected => println!("Expected value is 5, actual is {expected}"),
        _ => println!("Value was something else"),
    }
    }

    ここでは数値 123 に対してマッチさせようとしており、最初のケースでその値が 5 かどうかを確認したいと考えています。素朴に考えると、値が 5 ではないため 最初のケースはマッチしないはずです。しかし実際には、これは常にマッチする 変数パターンとして解釈されるため、最初の分岐が常に選ばれます。 代わりに定数を使えば、期待どおりに動作します。

さらに詳しく

  • 受講者に紹介できるパターン構文の別の要素として、パターンの一部を変数に 束縛する @ 構文があります。たとえば次のようになります:

    #![allow(unused)]
    fn main() {
    // Copyright 2024 Google LLC
    // SPDX-License-Identifier: Apache-2.0
    
    let opt = Some(123);
    match opt {
        outer @ Some(inner) => {
            println!("outer: {outer:?}, inner: {inner}");
        }
        None => {}
    }
    }

    この例では、inner は分解によって Option から取り出した値 123 を 持っています。一方、outerSome(inner) 式全体をキャプチャするため、 完全な Option::Some(123) を含みます。これはめったに使われませんが、より 複雑なパターンでは役に立つことがあります。