模式匹配
一个模式通常由以下组件组成
- 字面量
- 结构的数组、枚举、结构体、元组
- 变量
- 通配符
- 占位符
组件用于描述数据的形状,根据数据的形状进而对值进行匹配
所有可以使用模式的场合
match分支
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
match表达式必须穷尽匹配值的所有可能性
if let 表达式
可以混合使用 if let、else if 和 else if let 表达式,提供更大的灵活性
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");
}
}
while let 条件循环
反复执行同一个模式匹配直到匹配失败,自动退出循环
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
for 循环
for 模式 in 值(通常是一个迭代器的返回值)
例:迭代适配器enumerate
会生成一个包含索引和值本身的元组
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
let 语句
实际上,所有的let语句都是模式匹配
let 模式 = 表达式;
比如之前我们使用过的解构元组
let (x,y,z) = (1,2,3);
函数的参数
传参过程是模式匹配(因为传参过程类似于一个赋值过程嘛)
比如通过模式匹配解构元组实参:
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Refutability :模式是否会匹配失败
- 不可失败的模式能够匹配任何传入的值
- 函数参数、let语句、for循环只能接受不可失败模式
- 可失败模式则可能因为某些特定的值而匹配失败
- if let 和 while let只能接受可失败模式
模式语法
匹配字面量
匹配时要求的是“全等”
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => 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"),
}
匹配区间
使用a..=b
表示匹配一个a到b的闭区间,比如1..=4
就等同于1|2|3|4
范围模式只被允许使用数值或者char值
使用解构来分解值
解构结构体
使用语法
结构体名 {
字段1名:模式1,
字段2名:模式2,
} = 结构体实例
来直接获得结构体内部的值
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: x, y: y } = p; 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称
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),
}
}
解构枚举
用于解构枚举的模式必须要对应枚举定义中存储数据的方式
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
)
}
}
}
解构嵌套的结构体和枚举
模式可以嵌套使用,只要能表达清楚数据的结构就好
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
)
}
_ => ()
}
}
解构结构体和元组
let ((feet,inches),Point {x,y}) = ((3,10),Point {x:-1,y:10});
忽略模式中的值
使用_
忽略整个值
比如为结构体实现trait时,trait的方法包含了不需要的参数,就用得上忽略模式了
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {}", y);
}
fn main() {
foo(3, 4);
}
使用嵌套的_
忽略值的某些部分
把被忽略的部分用_
补上就好
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {}, {}, {}", first, third, fifth)
},
}
通过以_
开头的名称来忽略未使用的变量
如果一个变量定义了却未被使用,会触发警告
使用_
开头则可以忽略未使用的变量
- 与
_
的区别:_
不会执行变量绑定,而_变量名
则会执行变量绑定
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);
使用..
忽略值的剩余部分
..
模式可以忽略一个值中没有被我们显式匹配的部分
-
..
会自动展开并填充任意多个值let numbers = (2, 4, 8, 16, 32); match numbers { (first, .., last) => { println!("Some numbers: {}, {}", first, last); }, }
-
使用时不能出现歧义,比如下面这个例子就是有歧义
let numbers = (2, 4, 8, 16, 32); match numbers { (.., second, ..) => {
使用匹配守卫添加额外条件
匹配守卫(match guard)是一个指定于 match 分支模式之后的额外 if 条件,它也必须被满足才能选择此分支。
let num = Some(4);
match num {
Some(x) if x < 5 => println!("less than five: {}", x),
Some(x) => println!("{}", x),
None => (),
}
@
绑定
运算符(@)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式
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)
},
}