スポンサーリンク

Rustのパターンマッチングについて学んだ話

Rustは構文のレベルでパターンマッチングをする言語で、しかも至る所でそれが出てくるので、パターンマッチングを受け入れないとコードを読めるようにならない。

例えばmatch文。

fn main() {

    let x:i32=5;
    let y:i32=10;

    // 「(5 , 10) というパターン」
    match (x,y){

        // (5,10) の内容が 「(5,10)」 に一致する場合
        (5,10)=> println!("Both x and y are 5 and 10, respectively."),

        // (5,10) の内容が 「(5,何でもいい)」 に一致する場合
        (5,_)=>  println!("x is 5, and y can be any value."),

        // (5,10) の内容が 「上記のパターン以外」 に一致する場合
        _ =>     println!("Neither of the above patterns match x and y."),
    }


    print!("{} {}",x,y);

}

これはなんとなくわかる。

ところがRustには「パターンにマッチするか」だけではなく、「マッチして、それが変数だったらそこに値を拘束する」という機能もある。

つまり以下は、

・「パターンマッチ」

・「value_of_yの変数宣言」

・「value_of_yへのyの代入」

が同時に行われている。

fn main() {

    let x:i32=5;
    let y:i32=7;

    // 「(5 , 10) というパターン」
    match (x,y){

        (5,10)=> println!("Both x and y are 5 and 10, respectively."),

        // パターンマッチングを行い、マッチした場合は値の拘束(変数の定義と代入)も行う
        (5,value_of_y )=>  println!("x is 5, and y is {}",value_of_y),

        _ =>     println!("Neither of the above patterns match x and y."),
    }

}

一事が万事この調子なので、変数を定義する機能だと思っていたletも、実はパターンマッチと、マッチした場合の値の拘束を同時に行う構文だということになる。

だからlet文で変数定義をするときに_ (ワイルドカード)が使えてしまう。

fn main() {

    // 例1
    // (x,y)というパターンと(3,5)はマッチする
    // マッチするので、
    // x に 3 を拘束
    // y に 5 を拘束
    // この二つを同時に行う。
    let (x,y)=(3,5);


    // 例2
    // (z,何でもいい) というパターンと (7,9) はマッチする
    // マッチするから 7 が z に 拘束される
    // _ はワイルドカードなので無視される。
    let (z,_)=(7,9);

    println!("{} {} {} ",x,y,z);

}

let文の=の右辺と左辺でパターンマッチを行い、拘束可能な部分は拘束する。拘束できないところは無視する。

こんなもの何に使うんだと思うのだが、ある構造体なりタプルかなりから値を取り出したい、しかし全部取り出す必要はないときに、不要な変数宣言を省略できるという利点がある。

fn main() {

    // 三つの三次元座標を用いて三角形を一枚定義
    let triangle_points = [
         (0,3,9), // 頂点1
         (7,0,6), // 頂点2
         (4,7,2)  // 頂点3
    ];

    // この三角形を Z 平面に投影した二次元座標を取得したい
    for p in triangle_points{

        // 三要素の p から前二つだけ取り出し、x,yに拘束
        // 三つ目の要素は z に入れるべきだが、不要なので _ を指定して省略。つまり変数 z は定義しない。
        let (x,y,_) = p;

        println!("{} {}",x,y);

    }

}

Rust的には、「比較するものとされるものがマッチするならコンパイルが通る」わけで、当然構造体でも同じ理屈が通用する。

構造体の場合、無視の記号は .. を用いる。また構造体はフィールド名で値を選択するので、順番は気にしなくていい。

struct Point {
    x: i32,
    y: i32,
    z: i32,
}

fn main() {


  let point = Point { x: 0, y: 7, z: 9 };

  // point からy座標だけ取り出したいので
  // y_coord変数を定義し、 point.y を拘束
  // .. により、ほかのフィールドは無視する
  let Point { y:y_coord, .. } = point;

  // point.y が取れているか確認
  println!("y value is {}",y_coord);


}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: