注意: 最新版のドキュメントをご覧ください。この第1版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。

パターン

パターンはRustにおいて極めて一般的です。

パターンは 変数束縛マッチ文 などで使われています。

さあ、めくるめくパターンの旅を始めましょう!

簡単な復習:リテラルに対しては直接マッチさせられます。また、 _ は「任意の」ケースとして振る舞います。

fn main() { let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), } }
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 をシャドーイングします。

複式パターン

| を使うと、複数のパターンにマッチさせることができます:

fn main() { let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), } }
let x = 1;

match x {
    1 | 2 => println!("one or two"),
    3 => println!("three"),
    _ => println!("anything"),
}

これは、 one or two を出力します。

分配束縛

struct のような複合データ型が存在するとき、パターン内でその値を分解することができます。

fn main() { 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, y } => println!("({},{})", x, y),
}

値に別の名前を付けたいときは、 : を使うことができます。

fn main() { struct Point { x: i32, y: i32, } let origin = Point { x: 0, y: 0 }; match origin { Point { x: x1, y: y1 } => println!("({},{})", x1, y1), } }
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 をしてみましょう:

fn main() { let some_value: Result<i32, &'static str> = Err("There was an error"); match some_value { Ok(value) => println!("got a value: {}", value), Err(_) => println!("an error occurred"), } }
match some_value {
    Ok(value) => println!("got a value: {}", value),
    Err(_) => println!("an error occurred"),
}

最初の部分では Ok ヴァリアント内の値に value を束縛しています。しかし Err 部分では、ヴァリアント内のエラー情報を無視して一般的なエラーメッセージを表示するために _ を使っています。

_ は束縛を導入するどのようなパターンにおいても有効です。これは大きな構造の一部を無視する際に有用です。

fn main() { fn coordinate() -> (i32, i32, i32) { // generate and return some sort of triple tuple // 3要素のタプルを生成して返す (1, 2, 3) } let (x, _, z) = coordinate(); }
fn coordinate() -> (i32, i32, i32) {
    // 3要素のタプルを生成して返す
}

let (x, _, z) = coordinate();

ここでは、タプルの最初と最後の要素に xz を束縛します。

同様に .. でパターン内の複数の値を無視することができます。

fn main() { 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."), } }
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 と ref mut

参照 を取得したいときは ref キーワードを使いましょう。

fn main() { let x = 5; match x { ref r => println!("Got a reference to {}", r), } }
let x = 5;

match x {
    ref r => println!("Got a reference to {}", r),
}

これは Got a reference to 5 を出力します。

ここで match 内の r&i32 型を持っています。言い換えると、 ref キーワードはパターン内で使う参照を 作り出します 。ミュータブルな参照が必要な場合は、同様に ref mut を使います。

fn main() { let mut x = 5; match x { ref mut mr => println!("Got a mutable reference to {}", mr), } }
let mut x = 5;

match x {
    ref mut mr => println!("Got a mutable reference to {}", mr),
}

範囲

... で値の範囲をマッチさせることができます:

fn main() { let x = 1; match x { 1 ... 5 => println!("one through five"), _ => println!("anything"), } }
let x = 1;

match x {
    1 ... 5 => println!("one through five"),
    _ => println!("anything"),
}

これは one through five を出力します。

範囲は多くの場合、整数か char 型で使われます:

fn main() { let x = '💅'; match x { 'a' ... 'j' => println!("early letter"), 'k' ... 'z' => println!("late letter"), _ => println!("something else"), } }
let x = '💅';

match x {
    'a' ... 'j' => println!("early letter"),
    'k' ... 'z' => println!("late letter"),
    _ => println!("something else"),
}

これは something else を出力します。

束縛

@ で値に名前を束縛することができます。

fn main() { let x = 1; match x { e @ 1 ... 5 => println!("got a range element {}", e), _ => println!("anything"), } }
let x = 1;

match x {
    e @ 1 ... 5 => println!("got a range element {}", e),
    _ => println!("anything"),
}

これは got a range element 1 を出力します。 データ構造の一部に対して複雑なマッチングをしたいときに有用です:

fn main() { #[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), _ => {} } }
#[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 を束縛します。

@| と組み合わせて使う場合は、それぞれのパターンで同じ名前が束縛されるようにする必要があります:

fn main() { let x = 5; match x { e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e), _ => println!("anything"), } }
let x = 5;

match x {
    e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e),
    _ => println!("anything"),
}

ガード

if を使うことでマッチガードを導入することができます:

fn main() { 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."), } }
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| の両側に適用されます:

fn main() { let x = 4; let y = false; match x { 4 | 5 if y => println!("yes"), _ => println!("no"), } }
let x = 4;
let y = false;

match x {
    4 | 5 if y => println!("yes"),
    _ => println!("no"),
}

これは no を出力します。なぜなら if4 | 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 } => ...
}

パターンはとても強力です。上手に使いましょう。