パターンマッチング
Essential**パターン(pattern)**は同時に2つのことをする。値が特定の形状を持つかどうかをテストし、その値の一部を名前にバインドする。パターンはRustの中で値が導入されるあらゆる場所に現れる。
let でのパターン
最もシンプルなパターンは名前だ——何にでもマッチし、バインドする。
let x = 42;
タプルやstructは直接デストラクチャ(destructure)できる。
let (a, b) = (1, 2);
struct Point { x: f64, y: f64 }
let p = Point { x: 3.0, y: 4.0 };
let Point { x, y } = p;
match でのパターン
match はパターンがその力を発揮する場所だ。各アームは パターン => 式 という形で、match は網羅的(exhaustive)——あらゆる可能な値を処理しなければならない。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
fn process(msg: Message) {
match msg {
Message::Quit => println!("quit"),
Message::Move { x, y } => println!("move to {x},{y}"),
Message::Write(text) => println!("write: {text}"),
}
}
Message にバリアントを追加すると、キャッチオールのない match は新しいバリアントを処理するまでコンパイルを拒否する。網羅性がコンパイラをチェックリストに変える。
match は式でもあり、値を生成する。
let label = match n {
0 => "zero",
1..=9 => "single digit",
_ => "large",
};
ワイルドカード _ は何にでもマッチし、それを捨てる。
よくあるパターンの形式
| パターン | マッチする対象 |
|---|---|
name | 何でも。name にバインド |
_ | 何でも。捨てる |
42 / true / 'a' | その特定のリテラル |
1..=9 | その閉区間の範囲 |
(a, b) | 2要素タプル。a と b をバインド |
Point { x, y } | struct。フィールドをバインド |
Some(v) | Some バリアント。内部をバインド |
None | None バリアント |
Foo::Bar(x, y) | ペイロード付きのenumバリアント |
パターンは任意に入れ子にできる。
match pair {
(Some(x), Some(y)) if x == y => println!("equal: {x}"),
(Some(x), _) => println!("left: {x}"),
(_, Some(y)) => println!("right: {y}"),
_ => println!("neither"),
}
ガード
アームはパターンの後に追加条件を付けてマッチを絞り込める。
match n {
x if x < 0 => println!("negative: {x}"),
0 => println!("zero"),
x => println!("positive: {x}"),
}
ガード if x < 0 はパターン x がすでにマッチした場合にのみ実行される。ガードが失敗しても match は終わらない——次のアームに続く。
if let と while let
1つのバリアントだけが重要な場合、if let は完全な match を避けられる。
if let Some(value) = optional {
println!("got {value}");
}
while let はパターンがマッチし続ける間ループする。
while let Some(top) = stack.pop() {
println!("{top}");
}
どちらも、意味のある1つのアームと _ => () を持つ match の糖衣構文(syntactic sugar)だ。
関数パラメータでのパターン
パラメータもパターンだ。
fn first((x, _): (i32, i32)) -> i32 {
x
}
これが必要になることは稀だが、パターンが let と match だけでなく、値がバインドされるあらゆる場所に現れることを示している。