模式匹配
- 模式匹配通常由一下组件组成:
字面量。
解构的数组、枚举、结构体或者元组。
变量。
通配符
占位符。
知识汇总
模式的不可失败性和可失败性
- 模式可以被分为不可失败(irrefutable) 和可失败(refutable)两种类型。
- 例如 let x = 5; 中的 x 便是不可失败模式,因为它能够匹配表达式右侧的所有可能的返回值。
- 例如 if let Some(x) = a_value 中的 Some(x) 便是可失败模式,如果 a_value 变量的值时 None 而不是 Some,那么表达式左侧的Some(x) 模式就会发生不匹配。
- 了解这些概念十分重要,比如 let Some(x) = a_value 就会造成编译困扰,因为这个表达式是可失败的那么就要放到 if 中去。
if let 表达式
- 举例:
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age : Result = "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");
}
}
}
while let 条件循环模式匹配
- 例子:while let 会反复执行同一个模式匹配直到出现失败的情况。
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(s) = stack.pop() {
println!("Stack value : {}", s);
}
}
for 循环进行元祖填充
- 例子:注意不要忘记调用 enumerate 生成一个元祖迭代器
fn main() {
let v = vec!["a", "b", "c"];
for (index, value) in v.iter().enumerate() {
println!("index : {}, value : {}",index, value );
}
}
let 关键字本身就是一种模式匹配的方式
- 例子:
// 如果模式元素中的数量与元祖的数量不符,那么就会导致匹配失败。
let (x, y, z) = (1, 2, 3);
// 如果确实有的不需要,可以通过 _ 或者 .. 进行省略
let (x, y, _) = (1, 2, 3);
函数参数也是模式匹配
- 可以看到模式匹配无处不在,函数的参数也是模式匹配。
- 例子:
// 实际上定义了一个元祖参数
fn print_coordinates(&(x, y): &(i32, i32)) {
println!(" Current location : ({}, {})", x, y);
}
fn main() {
let point = (55, 33);
print_coordinates(&point);
}
匹配字面量
- 例子:
fn main() {
let x = 1 ;
match x {
1 => {println!("One"); },
2 => {println!("Two"); },
// 注意下面必须加上如果不加会提示没有穷尽所有解
_ => {println!("Other"); },
}
}
匹配命名变量
- 例子:
fn main() {
let x = Some(5);
let y = 10 ;
match x {
Some(50) => println!("Get 50"),
Some(y) => println!("Matched, y = {:?}", y ),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end : x = {:?}, y = {:?}", x, y);
}
多重匹配
- 例子:
fn main() {
let x = 1 ;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything."),
}
}
使用 ... 来匹配区间
- 例子:
fn main() {
let x = 7;
match x {
1 ... 9 => println!("one through nine"),
3 => println!("three"),
_ => println!("anything."),
}
}
使用 ..= 来匹配字符区间
- 例子:
fn main() {
let x = 'c';
match x {
'a' ..= 'd' => println!("a | b | c | d"),
'f' ..= 'i' => println!("f | g | h | i"),
_ => println!("anything."),
}
}
使用解构来分解值(分解结构)
- 例子:
fn main() {
struct Point {
x: i32,
y: i32,
}
let point = Point {
x: 32,
y: 66
};
// 解构
let Point {x : a, y : b } = point;
println!("x:{} , y:{}", a, b);
}
稍稍复杂一点的match 匹配
- 看了这个例子突然感觉模式匹配很有用,会的:
fn main() {
struct Point {
x: i32,
y: i32,
}
let point = Point {
x: 0,
y: 7
};
// 进行更复杂一点的模式匹配
match point {
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),
}
}
解构嵌套的结构体和枚举:
- 例子:
enum Color {
Rgb (i32,i32,i32),
Hvs (i32,i32,i32),
}
enum Message {
Quit,
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Rgb(6,6,6));
match msg {
Message::ChangeColor(Color::Rgb(r,g,b)) => {
println!("r={},g={},b={}", r,g,b);
},
Message::ChangeColor(Color::Hvs(h,v,s)) => {
println!("h={},v={},s={}", h,v,s);
},
_ => {}
}
}
使用 _ (下划线) 忽略变量值时需要特别注意的一个问题
- 例子,错误的
fn main() {
let s = Some(String::from("Hello."));
if let(_s) = s {
println!("Hello , into there.")
}
// 这段代码编译不过去,虽然 _s 但是所有权仍然会转移
println!("s value is : {:?} ", s);
}
- 例子,正确的,_ 单纯的下滑线就不会造成所有权转移,稍稍修改一下。
fn main() {
let s = Some(String::from("Hello."));
// 修改一下这里,去掉 _ 后面的任何值,所有权就不会转移。
if let(_) = s {
println!("Hello , into there.")
}
println!("s value is : {:?} ", s);
}
使用 .. (双点) 忽略匹配值
- 例子:
struct Point {
x: i32, y:i32 , z:i32,
}
fn main() {
let p = Point {x:3,y:4,z:5};
// 如果只是想匹配x 可以使用双点语法。
let Point{x, ..} = p;
assert_eq!(3, x);
}
为了匹配更复杂的模式可以使用match guard
- 就是在模式匹配中新增一个 if 条件,举例:
fn main() {
let num = Some(4);
match num {
Some(x) if x < 5 => { println!("Num {} less than five. ", x) },
Some(x) => { println!("Num is {} . ", x) },
None => {},
}
}
- 注意一个情况:
match x {
4 | 5 | 6 if y => { ... }
}
实际上等于
match x {
(4 | 5 | 6) if y => { ... }
// 而不是
4 | 5 | (6 if y) => { ... }
}
模式匹配中的绑定
- 有些范围匹配即便成功了,我们也无法捕获到底是什么值匹配成功的。
- 这时候就需要在匹配中绑定变量需要通过 @ 运算符。
fn main() {
enum Message {
Hello {id: i32}
}
let msg = Message::Hello {id: 5};
match msg {
// 注意这里,如果不用 id_variable@ 进行变量捕获,下面的println 就无法直到具体的值是什么。
Message::Hello {id: id_variable@ 3..=7} => {
println!("Found an id in range : {} ", id_variable);
},
Message::Hello {id:10...12} => {
println!("Found id between 10 to 12");
},
Message::Hello {id} => {
println!("Found some other id : {}", id);
},
}
}
结束
- 感谢阅读,See you at work.