Rust
match
は6章で軽くやりました。今回は、より深くやる回です
パターンは、複雑であれ、単純であれ、Rustで型の構造に一致する特別な記法です。
本題.
パターンは値の構造に合致する
match VALUE { PATTERN => EXPRESSION, PATTERN => EXPRESSION, PATTERN => EXPRESSION, }
match
式PATTERN => EXPRESSION
がアームPATTERN
がパターンfn main() { let favorite_color: Option<&str> = None; let is_tuesday = false; let age: Result<u8, _> = "34".parse(); if let Some(color) = favorite_color { println!("Using your favorite color, {}, as the background", color); } else if is_tuesday { println!("Tuesday is green day!"); } else if let Ok(age) = age { if age > 30 { println!("Using purple as the background color"); } else { println!("Using orange as the background color"); } } else { println!("Using blue as the background color"); } }
if let PATTERN=hoge {}
の形式#![allow(unused_variables)] fn main() { let mut stack = Vec::new(); stack.push(1); stack.push(2); stack.push(3); while let Some(top) = stack.pop() { println!("{}", top); } }
while let PATTERN
の形式#![allow(unused_variables)] fn main() { let v = vec!['a', 'b', 'c']; for (index, value) in v.iter().enumerate() { println!("{} is at index {}", value, index); } }
for PATTERN in ..
の形式let x = 5;
は
let PATTERN = EXPRESSION;
に対応したパターンらしい
このパターンは実質的に「値が何であれ、全てを変数xに束縛しろ」を意味します。
let (x, y, z) = (1, 2, 3);
とかもパターン
他の言語と違って括弧()は必須
fn foo(x: i32) { // code goes here }
fn print_coordinates(&(x, y): &(i32, i32)) { println!("Current location: ({}, {})", x, y); } fn main() { let point = (3, 5); print_coordinates(&point); }
fn hoge(PATTERN){}
の形式let x = 5;
でのx
if let Some(x) = a_value
でのSome(x)
let x = 5
if let x = a_value{ .. }
(a,b)
: タプルlet Some(x) = 5
if let Some(x) = a_value{ .. }
Some
の周辺以外は存在しない?これが
let some_u8_value = Some(0u8); match some_u8_value { Some(3) => println!("three"), _ => (), }
こう書ける
let some_u8_value = Some(0u8); if let Some(3) = some_u8_value { println!("three"); }
https://doc.rust-jp.rs/book-ja/ch06-03-if-let.html
if letを使うと、タイプ数が減り、インデントも少なくなり、定型コードも減ります。しかしながら、 matchでは強制された包括性チェックを失ってしまいます。matchかif letかの選択は、 特定の場面でどんなことをしたいかと簡潔性を得ることが包括性チェックを失うのに適切な代償となるかによります。
let x = 1; match x { 1 => println!("one"), // 1 2 => println!("two"), // 2 3 => println!("three"), // 3 _ => println!("anything"), // なんでも }
fn main() { let x = Some(5); let y = 10; match x { Some(50) => println!("Got 50"), Some(y) => println!("Matched, y = {:?}", y), _ => println!("Default case, x = {:?}", x), } println!("at the end: x = {:?}, y = {:?}", x, y); }
let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), }
let x = 5; match x { 1 ... 5 => println!("one through five"), _ => println!("something else"), }
1...5
は1から4にmatchする1 | 2 | 3 | 4
でも同じ1...=4
でも同じ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); }
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 => { // Quit列挙子には分配すべきデータがない println!("The Quit variant has no data to destructure.") }, Message::Move { x, y } => { println!( // x方向に{}、y方向に{}だけ動く "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 ) } } }
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();
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });
気が利くなあ
_
で値全体を無視するfn foo(_: i32, y: i32) { println!("This code only uses the y parameter: {}", y); } fn main() { foo(3, 4); }
トレイトを実装する際、 特定の型シグニチャが必要だけれども、自分の実装の関数本体では引数の1つが必要ない時など
そうすれば、代わりに名前を使った場合のようには、未使用関数引数についてコンパイラが警告することはないでしょう。
_
で値の一部を無視するlet mut setting_value = Some(5); let new_setting_value = Some(10); match (setting_value, new_setting_value) { (Some(_), Some(_)) => { // 既存の値の変更を上書きできません println!("Can't overwrite an existing customized value"); } _ => { setting_value = new_setting_value; } }
2番目のアームの_パターンで表現される他のあらゆる場合(setting_valueとnew_setting_valueどちらかがNoneなら)には、 new_setting_valueにsetting_valueになってほしいです。
_
で始めて未使用変数を無視するfn main() { let _x = 5; let y = 10; }
let s = Some(String::from("Hello!")); if let Some(_s) = s { println!("found a string"); } println!("{:?}", s);
解決するには:
let s = Some(String::from("Hello!")); if let Some(_) = s { println!("found a string"); } println!("{:?}", s);
..
で値の残りの部分を無視するstruct Point { x: i32, y: i32, z: i32, } let origin = Point { x: 0, y: 0, z: 0 }; match origin { Point { x, .. } => println!("x is {}", x), }
Point { .., x, .. }
とかはエラーになりますlet robot_name = Some(String::from("Bors")); match robot_name { Some(name) => println!("Found a name: {}", name), None => (), } println!("robot_name is: {:?}", robot_name);
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);
&
が使えないのでref
をつかう
let num = Some(4); match num { // 5未満です: {} Some(x) if x < 5 => println!("less than five: {}", x), Some(x) => println!("{}", x), None => (), }
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); }
let x = 4; let y = false; match x { 4 | 5 | 6 if y => println!("yes"), _ => println!("no"), }
(4 | 5 | 6) if y
であって4 | 5 | (6 if y)
でない@
束縛enum Message { Hello { id: i32 }, } 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) }, }