はじめに

Rustの基本的な文法についてまとめてみた。第6回!第5回の続き。
今回からはプログラムの構文として、様々なことに使われる「分岐」について。
C/C++, python ,java, rubyなどでも多くに使われる。

目的

Rustにおける基本的な構文の理解をする。

Rustの基本構文シリーズ

第1回⇒Rustの基本構文(1)  URL:https://qiita.com/jin237/items/b344d3a7e6dfe1056e31
第2回⇒Rustの基本構文(2)  URL: https://qiita.com/jin237/items/6910b4f803a35f317213
第3回⇒Rustの基本構文(3)  URL:https://qiita.com/jin237/items/59ef229a4de30cb8203b
第4回⇒Rustの基本構文(4)  URL: https://qiita.com/jin237/items/42249f984746711be0e2
第5回⇒Rustの基本構文(5)  URL:https://qiita.com/jin237/items/3fc3341effb3670e5865

match式による分岐(基本構文)

パターンマッチ

パターンマッチという仕組みの実現のために使用する。パターンマッチとはあるパターンに対して任意のパターンがマッチした時に実行する

ソースコード

例を引用してくる。(from 実践Rust入門 言語仕様から開発手法まで P.232-233 例1)

//例1
fn main(){
    //変数定義
    let value = 100;

    //valueと一致するものの判別
    match value {
        //Value=1ならばOneと表示
        1 => println!("One"),
        //以下他条件
        10 => println!("Ten"),
        100 => println!("One hundred"),
        _ => println!("Something else"),
    }
}

結果

[Running] cd "c:\Users\[usename]\rust\project1\" && rustc match_function1.rs && "c:\Users\[username]\rust\project1\"match_function1
One hundred

[Done] exited with code=0 in 1.379 seconds

この例で簡単な使い方はわかるとおもう。

簡単なコード説明

最初に定義したものに対して、その条件に一致するかどうかを見る。
それらの条件に当てはまらない場合の条件も付けている。今回のコードではパターンとしてリテラルの使用をしている。パターンに対してリテラルを使用している場合、検査される値とリテラル値の同一によってマッチする。
各種パターンを用意するのも重要であるが、それに当てはまらない部分の事象についてもしっかりと書かなければならない。書かない場合はコンパイルできない!(正確に言うと、パターンの列挙によりとり得るすべてのパターンを網羅してれば、かかなくてもよい。)
上記のコードであれば、

_ => println!("Something else"),

が必要となる。”_”によって、”それ以外”のという意味でとれるようになる。すなわち、アンダースコアに対しては、どの値に対してもマッチするということになる。ちなみに、アンダースコアの後に何かを記すこともできるために文章として意味が変わらずに示せるようになる。

_no_score => println!("Something else"),

というように書き換えることもできる。let valueを12にしてやってみる。

//例1
//change value number.
fn main(){
    //変数定義
    let value = 12;

    //valueと一致するものの判別
    match value {
        1 => println!("One"),
        10 => println!("Ten"),
        100 => println!("One hundred"),
        _no_score => println!("Something else"),
    }
}

結果

[Running] cd "c:\Users\[username]\rust\project1\" && rustc match_function1.rs && "c:\Users\[username]\rust\project1\"match_function1
Something else

[Done] exited with code=0 in 3.414 seconds

のようにアンダースコアの後に何かを表記してわかりやすくすることもできる。

優先順位について(実験も含めて)

検査するには優先順位があり、基本的に上の行から優先的に検査をされていく。
ということでパターン入れ替えをしてみる。

//例1
fn main(){
    //変数定義
    let value = 100;

    //valueと一致するものの判別
    match value {
        1 => println!("One"),
        _no_score => println!("Something else"),
        10 => println!("Ten"),
        100 => println!("One hundred"),
    }
}

ここで、
検査1)1とマッチするか
検査2)どの値にもマッチする”_”
検査3)10とマッチするか
検査4)100とマッチするか
の順にしてみた。本来、”100”とマッチするはずであるが、優先順位の違いによって以下のように結果が出てしまう。

[Running] cd "c:\Users\[username]\rust\project1\" && rustc match_function1.rs && "c:\Users\[username]\rust\project1\"match_function1
warning: unreachable pattern
  --> match_function1.rs:10:9
   |
9  |         _no_score => println!("Something else"),
   |         ---- matches any value
10 |         10 => println!("Ten"),
   |         ^^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

warning: unreachable pattern
  --> match_function1.rs:11:9
   |
9  |         _no_score => println!("Something else"),
   |         ---- matches any value
10 |         10 => println!("Ten"),
11 |         100 => println!("One hundred"),
   |         ^^^ unreachable pattern

Something else

[Done] exited with code=0 in 0.977 seconds

予想通りである。実際に何かを作るときにmatch式を使用するときにはこれには気を付けなければならない。

網羅性

アンダースコアによる、ほかのパターンを網羅できるように書いていたが、ある定義などをしていればする必要性はないことだけ軽く書いておく。

//例5 改編
enum Color{
    Red, Yellow, Green, Blue
}
fn main() {
    let color = Color::Red;
    let action = match color{
        Color::Red => "strawberry",
        Color::Yellow => "paprika",
        Color::Green => "green tea",
        Color::Blue => "sea",
    };

    println!("Red: {}", action);
}

このコード自体は、Warningが出るが、Red以外をenumに置いていないければ、let action内でのパターンマッチは正常に動かない。
Warningが出るのは、他の色についての処理がないためであり、そこまで重要視しなくてよい。

ソースコードの書き換え

最初のソースコードの書き換えをしてみる。

fn main(){
    let value = 100;

    let string = match value {
        1 => "One",
        10 => "Ten",
        100 => "One hundred",
        _ => "Something else",
    };

    println!("{}",string);
}

変数valueに対して、変数stringを定義して、値同士でのパターンマッチを行う。その中で、マッチしたものをprintln!によって表示するものである。この考え方によって、一つ一つの処理がこのコード量では誤差はないが、多量になった場合、処理時間が変わってくる。この結果はもちろん”One hundred”と表示される。

さいごに

今回は、match式について書いてきたが、まだパターンについて書ききれていないことがある。(分配束縛、match式の応用など)
これらについては次回の記事で書いていこうと思う。