📌 match式
パターンマッチング は,式の値がパターンに一致するかしないかを判定する仕組みです. match
式は,パターンマッチングによって評価する式を変えるときに使います.
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
match
式はパターンと式を =>
で結合したものを並べたものです.この パターン => 式
のことをアーム(arm)と言います. match
式はアームのパターンを順番に処理していき,パターンに一致した最初の式を評価してその結果を返します.そして,パターンに一致したアーム以降は処理されません.このような仕組みを短絡評価またはショートサーキットと呼びます
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
match
式はマッチング対象のオブジェクトが取りうる値をすべて網羅しなければなりません.そのため,記述したアーム以外に一致するワイルドカード(_
)を使うことができます.
let some_u8_value = 0u8;
match some_u8_value {
1 => println!("one"),
3 => println!("three"),
5 => println!("five"),
7 => println!("seven"),
_ => (),
}
Option
型が束縛しているオブジェクトをmatch
式で取り出せます.ここで注意することは,match
式でパターンが一致したときに,束縛しているオブジェクトを受け取りますが,そのとき所有権も移動している、ということです.
fn main() {
let a: Option<String> = Some(String::from("hello"));
match a {
Some(x) => println!("{}", x), // move ownership
None => ()
}
println!("{:?}", a);
// ^ value borrowed here after partial move
// error[E0382]: borrow of partially moved value: `a`
}
これは何が起きているかと言うと,変数 a
が束縛している Option
型のオブジェクトが,内部で束縛しているオブジェクトの所有権をアームのパターンによって取り出され所有権が渡されています.これにより,変数 a
は束縛したままですが,その内部では何も束縛していないことになります.なので,部分移動(partial move)が発生していることになりエラーとなります.この部分移動に対応する方法として次の2つがあります.
1つはアームのパターンでオブジェクトを参照で受け取る方法です.注意することは,パターンで参照を取得するときは ref
を使います.可変参照なら ref mut
です.
fn main() {
let a: Option<String> = Some(String::from("hello"));
match a {
Some(ref x) => println!("{}", x), // reference
None => ()
}
println!("{:?}", a); // borrow check!! - OK
}
もう1つは,返り値としてオブジェクトを返すことです.
fn main() {
let a: Option<String> = Some(String::from("hello"));
let a = match a {
Some(x) => { println!("{}", x); Some(x) }
None => None,
};
println!("{:?}", a); // borrow check!! - OK
}
📌 マッチガード
マッチガードはmatch
式のアームのパターンに,さらに条件(if
)を加えることができるものです.これにより,より複雑なパターンを扱えます:
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
📌 パターンによる束縛
変数に束縛するとき、パターンを使って分解できたことを覚えていますか?この分解束縛のパターンでも参照を使えます.
struct Account { name: String, pass: String }
fn main() {
let a = Account { name: String::from("name"), pass: String::from("pass") };
let Account { name, pass } = a; // move ownership
println!("{} {}", name, pass); // borrow check!! - OK
println!("{} {}", a.name, a.pass); // borrow check!! - Error
}
上記のコードはパターンによる分解束縛時に所有権も移動してしまい,借用チェックでコンパイルエラーになります.その場合は,パターンに ref
を使って不変参照で受け取ることで借用チェックに通ることになります.
struct Account { name: String, pass: String }
fn main() {
let a = Account { name: String::from("name"), pass: String::from("pass") };
let Account { ref name, ref pass } = a; // reference
println!("{} {}", name, pass); // borrow check!! - OK
println!("{} {}", a.name, a.pass); // borrow check!! - OK
}
📌 if let 式
match式のアーム(ワイルドカード以外)が1つのときはif let
式を使うと短く記述できます.例えば、次のコードを見てください.
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
if let
式を使うと次のように書けます.
if let Some(3) = some_u8_value {
println!("three");
}
if let
式と同様にwhile let
式も使えます.