値のマッチング
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 を 持っています。一方、outerはSome(inner)式全体をキャプチャするため、 完全なOption::Some(123)を含みます。これはめったに使われませんが、より 複雑なパターンでは役に立つことがあります。