注意: 最新版のドキュメントをご覧ください。この第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 } => ... }
パターンはとても強力です。上手に使いましょう。