注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
パターンはRustにおいて極めて一般的です。
さあ、めくるめくパターンの旅を始めましょう!
簡単な復習:リテラルに対しては直接マッチさせられます。また、 _ は「任意の」ケースとして振る舞います。
let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), }
これは one を表示します。
パターンには一つ落とし穴があります。新しい束縛を導入する他の構文と同様、パターンはシャドーイングをします。例えば:
fn main() { let x = 'x'; let c = 'c'; match c { x => println!("x: {} c: {}", x, c), } println!("x: {}", x) }let x = 'x'; let c = 'c'; match c { x => println!("x: {} c: {}", x, c), } println!("x: {}", x)
これは以下のように出力します。
x: c c: c
x: x
別の言い方をすると、 x => は値をパターンにマッチさせ、マッチの腕内で有効な x という名前の束縛を導入します。既に x という束縛が存在していたので、新たに導入した x は、その古い x をシャドーイングします。
| を使うと、複数のパターンにマッチさせることができます:
let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), }
これは、 one or two を出力します。
struct のような複合データ型が存在するとき、パターン内でその値を分解することができます。
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x, y } => println!("({},{})", x, y), }
値に別の名前を付けたいときは、 : を使うことができます。
struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x: x1, y: y1 } => println!("({},{})", x1, y1), }
値の一部にだけ興味がある場合は、値のすべてに名前を付ける必要はありません。
fn main() { struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x, .. } => println!("x is {}", x), } }struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x, .. } => println!("x is {}", x), }
これは x is 0 を出力します。
最初のメンバだけでなく、どのメンバに対してもこの種のマッチを行うことができます。
fn main() { struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { y, .. } => println!("y is {}", y), } }struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { y, .. } => println!("y is {}", y), }
これは y is 0 を出力します。
この「分配束縛」 (destructuring) と呼ばれる振る舞いは、 タプル や 列挙型 のような、任意の複合データ型で使用できます。
パターン内の型や値を無視するために _ を使うことができます。
例として、 Result<T, E> に対して match をしてみましょう:
match some_value { Ok(value) => println!("got a value: {}", value), Err(_) => println!("an error occurred"), }
最初の部分では Ok ヴァリアント内の値に value を束縛しています。しかし Err 部分では、ヴァリアント内のエラー情報を無視して一般的なエラーメッセージを表示するために _ を使っています。
_ は束縛を導入するどのようなパターンにおいても有効です。これは大きな構造の一部を無視する際に有用です。
fn coordinate() -> (i32, i32, i32) { // 3要素のタプルを生成して返す } let (x, _, z) = coordinate();
ここでは、タプルの最初と最後の要素に x と z を束縛します。
同様に .. でパターン内の複数の値を無視することができます。
enum OptionalTuple { Value(i32, i32, i32), Missing, } let x = OptionalTuple::Value(5, -2, 3); match x { OptionalTuple::Value(..) => println!("Got a tuple!"), OptionalTuple::Missing => println!("No such luck."), }
これは Got a tuple! を出力します。
参照 を取得したいときは ref キーワードを使いましょう。
let x = 5; match x { ref r => println!("Got a reference to {}", r), }
これは Got a reference to 5 を出力します。
ここで match 内の r は &i32 型を持っています。言い換えると、 ref キーワードはパターン内で使う参照を 作り出します 。ミュータブルな参照が必要な場合は、同様に ref mut を使います。
let mut x = 5; match x { ref mut mr => println!("Got a mutable reference to {}", mr), }
... で値の範囲をマッチさせることができます:
let x = 1; match x { 1 ... 5 => println!("one through five"), _ => println!("anything"), }
これは one through five を出力します。
範囲は多くの場合、整数か char 型で使われます:
let x = '💅'; match x { 'a' ... 'j' => println!("early letter"), 'k' ... 'z' => println!("late letter"), _ => println!("something else"), }
これは something else を出力します。
@ で値に名前を束縛することができます。
let x = 1; match x { e @ 1 ... 5 => println!("got a range element {}", e), _ => println!("anything"), }
これは got a range element 1 を出力します。
データ構造の一部に対して複雑なマッチングをしたいときに有用です:
#[derive(Debug)] struct Person { name: Option<String>, } let name = "Steve".to_string(); let mut x: Option<Person> = Some(Person { name: Some(name) }); match x { Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a), _ => {} }
これは Some("Steve") を出力します。内側の name の値への参照に a を束縛します。
@ を | と組み合わせて使う場合は、それぞれのパターンで同じ名前が束縛されるようにする必要があります:
let x = 5; match x { e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e), _ => println!("anything"), }
if を使うことでマッチガードを導入することができます:
enum OptionalInt { Value(i32), Missing, } let x = OptionalInt::Value(5); match x { OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), OptionalInt::Value(..) => println!("Got an int!"), OptionalInt::Missing => println!("No such luck."), }
これは Got an int! を出力します。
複式パターンで if を使うと、 if は | の両側に適用されます:
let x = 4; let y = false; match x { 4 | 5 if y => println!("yes"), _ => println!("no"), }
これは no を出力します。なぜなら if は 4 | 5 全体に適用されるのであって、 5 単独に対して適用されるのではないからです。つまり if 節は以下のように振舞います:
(4 | 5) if y => ...
次のようには解釈されません:
4 | (5 if y) => ...
ふう、マッチには様々な方法があるのですね。やりたいことに応じて、それらを混ぜてマッチさせることもできます:
fn main() { match x { Foo { x: Some(ref name), y: None } => ... } }match x { Foo { x: Some(ref name), y: None } => ... }
パターンはとても強力です。上手に使いましょう。