Rust Lang Book Ch.18 Patterns and Matching
Rust中的Pattern能够包含以下内容:
a) literal
b)Destructured arrays, enums, structs, or tuples
c) 变量
d) 通配符
e) placeholders
以下的都算是pattern:
a) match Arms
b) if let
c) while let
d) for x in y 中的x
e) let PATTERN = EXPRESSION
f) function parameters, 例如&(x, y): &(i32, i32)
Refutability
能够match任何传过来的值的是irrefutable pattern。例如let x = 5中的x。
与此对应的,有时可能match不了传过来的值的是refutable pattern,例如let Some(x) = some_option_value;
一般来说,写程序时可以不重视pattern的Refutable和irrefutable之间的差别,但是,在弹出错误信息的时候要能够区别。P.S: Rust只允许match的最多一个arm是irrefutable match。
Pattern Syntax
1. 和固定值(literal)做对比
let x = 1; match x { 1 => println!("one"), 2 => println!("two"), 3 => println!("three"), _ => println!("anything"), }
2. Named Variable
let y = 5中的y是一个named variable也是一个irrefutable pattern。
但是named variable也可能只是pattern的一部分。
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); //Output: //Matched, y = 5
//注意在这里match内是一个新的作用域了,里面的Some(y)里面的y是个named variable,与外层的y=10无关
Multiple Patterns
可以用|把多个pattern连起来。
let x = 1; match x { 1 | 2 => println!("one or two"), 3 => println!("three"), _ => println!("anything"), }
Matching Ranges
使用..=可以一次性匹配一个全闭区间。..=只能和数字或者char搭配使用。
let x = 5; match x { 1..=5 => println!("one through five"), _ => println!("something else"), }
let x = 'c'; match x { 'a'..='j' => println!("early ASCII letter"), 'k'..='z' => println!("late ASCII letter"), _ => println!("something else"), }
Destructuring
Destructuring Structs
可以带域名,直接按顺序取named variable,也可以在给一部分域名中嵌套其他pattern
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 Point {x, y} = p;
assert_eq!(0, x); }
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), } }
Destructuring Enums
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 ), } }
Destructuring Nested Structs and Enums
enum Color { Rgb(i32, i32, i32), Hsv(i32, i32, i32), } enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(Color), } fn main() { let msg = Message::ChangeColor(Color::Hsv(0, 160, 255)); match msg { Message::ChangeColor(Color::Rgb(r, g, b)) => println!( "Change the color to red {}, green {}, and blue {}", r, g, b ), Message::ChangeColor(Color::Hsv(h, s, v)) => println!( "Change the color to hue {}, saturation {}, and value {}", h, s, v ), _ => (), } }
Destructuring Structs and Tuples
let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
Ignore
忽略整个待匹配值:_
fn foo(_: i32, y: i32) { println!("This code only uses the y parameter: {}", y); }
忽略部分待匹配值:嵌套的_
match (setting_value, new_setting_value) { (Some(_), Some(_)) => { println!("Can't overwrite an existing customized value"); } _ => { setting_value = new_setting_value; } }
忽略一个没使用过的变量而不输出警告:在变量名前面加_
fn main() { let _x = 5; let y = 10; }
忽略没匹配完的值: ..
match origin { Point { x, .. } => println!("x is {}", x), }
fn main() { let numbers = (2, 4, 8, 16, 32); match numbers { (first, .., last) => { println!("Some numbers: {}, {}", first, last); } } }
但是要避免二义性:
fn main() {
let numbers = (2, 4, 8, 16, 32);
//error: `..` can only be used once per tuple pattern
match numbers {
(.., second, ..) => {
-- ^^ can only be used once per tuple pattern
println!("Some numbers: {}", second)
},
}
}
Match Guards
match guard是match arm之后接着的一个if条件,要求match arm和if 都被满足时才能够匹配。能够支持比pattern更强大的条件。
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); }
注意与|连用时,相当于任何一个pattern匹配之后,都要额外匹配这个if语句。
let x = 4; let y = false; match x { 4 | 5 | 6 if y => println!("yes"),
//无论是4,5还是6都需要匹配if y _ => println!("no"), }
@Bindings
@操作符允许程序员在同一个pattern中把变量保存下来同时测试是否满足条件。基本方法是fieldname: alias @ test_pattern
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 } => { //没有使用@,为了10..=12这个测试,就无法保留named variable println!("Found an id in another range") } Message::Hello { id } => println!("Found some other id: {}", id), }