注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。
パターンはRustにおいて極めて一般的です。 パターンは 変数束縛、 マッチ文 などで使われています。 さあ、めくるめくパターンの旅を始めましょう!
簡単な復習:リテラルに対しては直接マッチ出来ます。
また、 _
は「任意の」ケースとして振る舞います。
let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), }
これは one
を表示します。
パターンには一つ落とし穴があります。 新しい束縛を導入する他の構文と同様、パターンはシャドーイングをします。 例えば:
fn main() { let x = 1; let c = 'c'; match c { x => println!("x: {} c: {}", x, c), } println!("x: {}", x) }let x = 1; let c = 'c'; match c { x => println!("x: {} c: {}", x, c), } println!("x: {}", x)
これは以下のように出力します。
x: c c: c
x: 1
別の言い方をすると、 x =>
は値をパターンにマッチさせ、 x
という名前の束縛を導入します。
この束縛はマッチの腕内で有効で、値は c
を取ります。
このマッチのスコープ外の 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
を束縛します。
_
はそもそも値に束縛されない、つまり値をムーブしないということは特筆に値します。
let tuple: (u32, String) = (5, String::from("five")); // この場合tupleはムーブされます。何故なら第2要素の文字列がムーブされているからです: let (x, _s) = tuple; // 次の行は「error: use of partially moved value: `tuple`」になります。 // println!("Tuple is: {:?}", tuple); // しかしながら、 let tuple = (5, String::from("five")); // この場合はtupleはムーブ _されません_ 。何故なら第2要素の文字列はムーブされず、第1要素のu32はCopyだからです: let (x, _) = tuple; // つまりこれは動きます: println!("Tuple is: {:?}", tuple);
またこれは、(訳注: 値に束縛されない)一時変数は文の終わりでドロップされるということでもあります。
fn main() { // Here, the String created will be dropped immediately, as it’s not bound: // 生成されたStringは変数を束縛しないので即座にドロップされる let _ = String::from(" hello ").trim(); }// 生成されたStringは変数を束縛しないので即座にドロップされる let _ = String::from(" hello ").trim();
複数の値を無視するのには ..
パターンが使えます。
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 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 } => ... }
パターンはとても強力です。上手に使いましょう。