すべての記法を網羅する。
成果物
参考
パターンの記法一覧
match
: リテラルmatch
: 名前付き変数match
:|
match
:...
- 分配:
struct
- 分配:
enum
- 分配: 参照
_
: パターン無視_
: 未使用ワーニング無視(変数名のプレフィクス)..
: 残りを無視ref
,ref mut
: パターンに参照を生成するmatch
: マッチガードmatch if
@
束縛
match
: リテラル
fn main() { let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), } }
$ rustc main.rs $ ./main one
match
: 名前付き変数
リテラルで使う。(_
は5
,6
以外のときすべて)
fn main() { let x = Some(5); let y = 10; match x { Some(5) => println!("Got 5"), Some(6) => println!("Got 6"), _ => println!("Default case, x = {:?}", x), } }
$ ./main Got 5
名前付き変数にすると同名の変数を覆い隠してしまう(シャドーイング)。パターンをSome(y) =>
としたとき、そのmatch
式のアーム=> println!("Matched, y = {:?}", y),
で参照できるy
は新たな変数である。つまりSome
をunwrap()
した値となる。だが、コードを見てみると、match
式の前で宣言されたlet y = 10;
のy
がある。これが参照できなくなってしまう。その証拠に、以下match
式で表示されるy
の値は10
でなく5
である。
fn main() { let x = Some(5); let y = 10; match x { Some(y) => println!("Matched, y = {:?}", y), _ => println!("Default case, x = {:?}", x), } println!("at the end: x = {:?}, y = {:?}", x, y); }
$ rustc main.rs $ ./main Matched, y = 5 at the end: x = Some(5), y = 10
シャドーイングでなく、外側のy
を使いたいなら、後述のマッチガードを使う。
match
: |
fn main() { let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), } }
$ rustc main.rs $ ./main one or two
|
はor
の意味。1 | 2
なら、1
または2
の場合にマッチする。
match
: ...
fn main() { let x = 5; match x { 1 ... 5 => println!("one through five"), _ => println!("something else"), } }
$ rustc main.rs $ ./main one through five
1 ... 5
は、1
〜5
の範囲ならマッチする。
- 範囲
...
は数値または文字のみで使える
分配: struct
struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 0, y: 7 }; let Point { x: a, y: b } = p; assert_eq!(0, a); assert_eq!(7, b); }
let 構造体名 { フィールド名: 変数名 } = 構造体インスタンス;
a
, b
が新しい変数。構造体のフィールドに対応させて変数名を指定してやることで、各変数に各フィールド値を代入する。
しかし、わざわざフィールド名と異なる名前をつけるのも面倒。フィールド名を変数名として使いまわすときの略記がある。以下。
struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 0, y: 7 }; let Point { x, y } = p; assert_eq!(0, x); assert_eq!(7, y); }
一部にのみ分配する方法もある。
struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 0, y: 7 }; match p { Point { x, y: 0 } => println!("On the x axis at {}", x), Point { x: 0, y } => println!("On the y axis at {}", y), Point { x, y } => println!("On neither axis: ({}, {})", x, y), } }
$ rustc main.rs $ ./main On the y axis at 7
分配: enum
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msg = Message::ChangeColor(0, 160, 255); match msg { Message::Quit => { println!("The Quit variant has no data to destructure.") }, Message::Move { x, y } => { println!( "Move in the x direction {} and in the y direction {}", x, y ); } Message::Write(text) => println!("Text message: {}", text), Message::ChangeColor(r, g, b) => { println!( "Change the color to red {}, green {}, and blue {}", r, g, b ) } } }
$ rustc main.rs ... $ ./main Change the color to red 0, green 160, and blue 255
match
式では全列挙子を網羅しないとエラーになる- 構造体なら持っているフィールドは名前付き変数にできる
- 定義と同じ名前にすれば略記できる
- タプル構造体なら変数の数が同じであるべき
分配: 参照
struct Point { x: i32, y: i32, } fn main() { let points = vec![ Point { x: 0, y: 0 }, Point { x: 1, y: 5 }, Point { x: 10, y: -3 }, ]; let sum_of_squares: i32 = points .iter() .map(|&Point { x, y }| x * x + y * y) .sum(); }
$ rustc main.rs $ ./main On the y axis at 7
もし.map(|&Point { x, y }| x * x + y * y)
が&Point
でなくPoint
ならエラーになっていた。参照でも値と同じように分配できる。
さらに複雑なパターンとして以下。タプルと構造体を含んだタプル。
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });
_
: パターン無視
_
はmatch
式の最後のアームとして役立つ。が、他のあらゆるパターンでも使える。
fn foo(_: i32, y: i32) { println!("このコードは引数yを使うだけです: {}", y); } fn main() { foo(3, 4); }
ユースケース例は以下。
トレイトを実装する際、 特定の型シグニチャが必要だけれども、自分の実装の関数本体では引数の1つが必要ない時
値の一部を確認するなら以下。(Some
内の値を使わないとき)
fn main() { let mut setting_value = Some(5); let new_setting_value = Some(10); match (setting_value, new_setting_value) { (Some(_), Some(_)) => { println!("既存の値の変更を上書きできません"); } _ => { setting_value = new_setting_value; } } println!("設定: {:?}", setting_value); }
特定の位置にある値を無視するなら以下。
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { (first, _, third, _, fifth) => { println!("Some numbers: {}, {}, {}", first, third, fifth) }, } }
$ rustc main.rs $ ./main Some numbers: 2, 8, 32
_
: 未使用ワーニング無視(変数名のプレフィクス)
コンパイルすると未使用の変数があったら警告が出る。だが、変数名の最初に_
を付与すれば警告が出ない。
fn main() { let _x = 5; let y = 10; }
$ rustc main.rs warning: unused variable: `y` --> main.rs:7:9 | 7 | let y = 10; | ^ help: consider prefixing with an underscore: `_y` | = note: #[warn(unused_variables)] on by default
変数_x
, y
はどちらも使っていない。なのに_x
は警告なし。_
プレフィックスで警告を消せる。
記法 | 違い |
---|---|
_ |
値を束縛しない |
_x |
値を束縛する |
以下、Some(_s)
とすることで変数s
の所有権が_s
にムーブしてしまう。
fn main() { let s = Some(String::from("Hello!")); if let Some(_s) = s { println!("found a string"); } println!("{:?}", s); }
error[E0382]: use of partially moved value: `s` --> main.rs:10:22 | 7 | if let Some(_s) = s { | -- value moved here ... 10 | println!("{:?}", s); | ^ value used here after move | = note: move occurs because the value has type `std::string::String`, which does not implement the `Copy` trait
これを避けるためにSome(_s)
をSome(_)
にするといい。_
はムーブしない。
$ ./main found a string Some("Hello!")
..
: 残りを無視
struct Point { x: i32, y: i32, z: i32, } fn main() { let origin = Point { x: 0, y: 0, z: 0 }; match origin { Point { x, .. } => println!("x is {}", x), } }
$ ./main x is 0
ポイントはPoint { x, .. }
。本来はPoint { x, y:_, z:_ }
とせねばらなかった。これを..
で省略した。
タプルの場合、以下のようなパターンで省略できる。
(first, ..)
(.., last)
(first, .., last)
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { // (first, ..) => println!("{}", first), // 2 // (.., last) => println!("{}", last), // 32 // (.., second, ..) => println!("{}", second), // error: `..` can only be used once per tuple or tuple struct pattern (first, .., last) => { println!("{}, {}", first, last); // 2, 32 }, } }
$ rustc main.rs $ ./main 2, 32
なお、(.., second, ..)
など複数ヶ所で..
を使うとエラーになる。
error: `..` can only be used once per tuple or tuple struct pattern --> main.rs:10:22 | 10 | (.., second, ..) => println!("{}", second), | ^^ can only be used once per pattern
ref
, ref mut
: パターンに参照を生成する
パターンだと所有権を奪われてしまう。
fn main() { let robot_name = Some(String::from("Bors")); match robot_name { Some(name) => println!("Found a name: {}", name),// error[E0382]: use of partially moved value: `robot_name` None => (), } println!("robot_name is: {:?}", robot_name); }
$ rustc main.rs error[E0382]: use of partially moved value: `robot_name` --> main.rs:11:37 | 8 | Some(name) => println!("Found a name: {}", name), | ---- value moved here ... 11 | println!("robot_name is: {:?}", robot_name); | ^^^^^^^^^^ value used here after move | = note: move occurs because the value has type `std::string::String`, which does not implement the `Copy` trait
そこでパターンにref
を用いると参照になり所有権を奪わない。
fn main() { let robot_name = Some(String::from("Bors")); match robot_name { Some(ref name) => println!("Found a name: {}", name), None => (), } println!("robot_name is: {:?}", robot_name); }
$ rustc main.rs $ ./main Found a name: Bors robot_name is: Some("Bors")
Some(&name)
とすればいい気がするが、それはできない。「分配: 参照」の項にあるとおり。
パターンとマッチさせている値に参照が含まれる場合、値から参照を分配する必要があり、 パターンに&を指定することでそうすることができます。
パターンの文脈では&
の意味が変わる。
パターンでは、Some(&name)
でなくSome(ref name)
とする。また、可変参照にしたくばSome(ref mut name)
とすれば良い。参照外しは*
演算子で行う。
let mut robot_name = Some(String::from("Bors")); match robot_name { Some(ref mut name) => *name = String::from("Another name"), None => (), }
パターン文脈では&
の意味が変わってしまうのが混乱する。
match
: マッチガード match if
fn main() { let num = Some(4); match num { Some(x) if x < 5 => println!("less than five: {}", x), Some(x) => println!("{}", x), None => (), } }
$ rustc main.rs $ ./main less than five: 4
パターン if 条件式 => 実行式
パターンがシャドーイングしてしまう問題を回避するために、マッチガードが使える。
fn main() { let x = Some(5); let y = 10; match x { Some(50) => println!("Got 50"), Some(n) if n == y => println!("Matched, n = {:?}", n), _ => println!("Default case, x = {:?}", x), } println!("at the end: x = {:?}, y = {:?}", x, y); }
$ rustc main.rs $ ./main Default case, x = Some(5) at the end: x = Some(5), y = 10
Some(n) if n == y
がポイント。上位スコープでlet y = 10;
と宣言している値を使っている。パターンでは別名n
としてy
と区別させる。その上でマッチガードif
を使えば、両者を比較した条件式により複雑なマッチが書ける。
|
と併用することもできる。4|5|6
にマッチし、かつif y
(y
がtrue
)のときのみマッチする。
fn main() { let x = 4; let y = false; match x { 4 | 5 | 6 if y => println!("yes"), _ => println!("no"), } }
$ rustc main.rs $ ./main no
@
束縛
enum Message { Hello { id: i32 }, } fn main() { let msg = Message::Hello { id: 5 }; match msg { Message::Hello { id: id_variable @ 3...7 } => { println!("Found an id in range: {}", id_variable) }, Message::Hello { id: 10...12 } => { println!("Found an id in another range") }, Message::Hello { id } => { println!("Found some other id: {}", id) }, } }
$ rustc main.rs $ ./main Found an id in range: 5
列挙体::列挙子 { id: 変数名 @ 3...7 }
: 列挙体にある列挙子の値である無名構造体がもつフィールドid
の値が、3
〜7
の間であればマッチする
これまたややこしい。以下2つの要件があるとき@ n ... m
表記が必要になる。
- 範囲内か判定したい
- パターンで変数名を宣言し、実行式の中で使いたい
もし範囲の変数が不要なら{ id: 10...12 }
とすればよく、@
は不要。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12(1)-release
- rustc 1.34.2 (6c2484dc3 2019-05-13)
- cargo 1.34.0 (6789d8a0a 2019-04-01)
$ uname -a Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux
前回まで
- Rustを学んでみたい(プログラミング言語)
- Rustの環境構築
- RustでHelloWorld
- Rustの和訳ドキュメント
- Cargoでプロジェクト作成・ビルド・実行
- クレートとは?
- Rustで関数を使ってみる
- Rustでモジュールを使ってみる
- Rustで乱数を生成する(rand)
- Rustで標準入力する(std::io::stdin().read_line())
- RustでMatch判定する(match)
- Rustでprintとread_lineを1行にする方法
- Rustで数当てゲーム
- クレート名にドット.が使えない
- Rustの変数と可変性(let, mut) error[E0384]: cannot assign twice to immutable variable
x
- Rustのimmutable束縛とconst定数の違い
- RustのREPL、evcxrのインストールに失敗した
- Rustでコンパイルするときの変数未使用warningを消す
- Rustの変数(再代入、再宣言(シャドーイング))
- Rustのシャドーイングについて
- イミュータブルについて(副作用)
- Rustの定数(const)
- Rustのデータ型(数値)
- Rustのデータ型(論理)
- Rustのデータ型(文字)
- Rustのデータ型(タプル)
- Rustのデータ型(配列)
- Rustの関数
- Rustのif式
- Rustのくりかえし文(loop)
- Rustのくりかえし文(while)
- Rustのくりかえし文(for)
- Rustの所有権(ムーブ)
- Rustの所有権(関数)
- Rustの所有権(スライス)
- Rustの構造体(定義とインスタンス化)
- Rustの構造体(プログラム例)
- Rustの構造体(メソッド)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustの列挙型(enum)
- Rustのmatch(制御フロー演算子)
- RustでNULLを扱う(Option, Some, None)
- NULL参照は10億ドルの失敗だった
- Rustの列挙型に独自表示を実装する(E0277 対策 std::fmt::Display 実装)
- RustのIfLet(matchの糖衣構文)
- Rustのプロジェクト構造
- Rustのcargoでライブラリ&テスト(単体、結合)
- Rustのモジュール(mod)
- Rustのモジュール(pub)
- Rustのmod参照方法(
mod 子モジュール名;
,use 要素名;
,extern crate クレート名;
,super
) - Rustのインポートまとめ(Rust2018)
- RustのコレクションVec型
- RustのコレクションString型
- RustのコレクションHashMap型
- Rustのコレクション(練習問題)
- Rustのエラー処理
- Rustのジェネリクス
- Rustのトレイト
- Rustのライフタイム1
- Rustのライフタイム2(構造体の定義)
- Rustのライフタイム3(ライフタイム省略)
- Rustのライフタイム4(impl定義)
- Rustの静的ライフタイム5('static)
- Rustのライフタイム6(ジェネリクス、トレイト境界とともに)
- Rustのテストコードを書く
- Rustのテスト実行
- Rustのテスト体系化
- Rustでコマンドライン引数を受け取る
- Rustのファイル読込
- Rustでリファクタリング(モジュール性とエラー処理の向上)
- Rustでテスト駆動開発
- Rustで環境変数を取得する
- RustでStdErr出力
- Rustのクロージャ
- Rustのイテレータ
- Rustのイテレータ(Minigrep改善)
- Rustのイテレータ(パフォーマンス)
- Rustのイテレータ(Minigrep改善)
- Rustのcargo(ビルドのカスタマイズ)
- Rustのcargo(cargo docでドキュメント生成)
- Rustのエクスポート(pub use)
- Rustのクレートを公開する方法(crates.io)
- Rustのcargoでワークスペースをつくる
- Rustのcargo installでバイナリをインストールする
- Rustのcargoを拡張する方法
- Rustのスマートポインタ
- スマートポインタBox
- Rustのスマートポインタ(Derefトレイト)
- Rustのスマートポインタ(Dropトレイト)
- Rustのスマートポインタ(Rc
) - Rustのスマートポインタ(RefCell
) - Rustのスマートポインタ(Weak
) - Rustのスレッド
- Rustのスレッド(メッセージ送受信)
- Rustのスレッド(Mutex、Arc)
- Rustのスレッド(Send、Syncトレイト)
- Rustのオブジェクト指向
- RustのOOP(トレイトオブジェクト)
- Rustのオブジェクト指向(デザインパターン)
- Rustのパターン
- Rustのパターン(論駁可能性)