18.1 能用到模式(匹配)的地方
18.1.1. 什么是模式
模式(pattern) 是 Rust 里一种特殊的语法,用于匹配复杂类型和简单类型的结构。
将模式与 match 表达式以及其他构造结合使用,可以更好地控制程序的流程。
模式由以下元素的一些组合构成:
- 字面值
- 解构的数组、
enum、struct和元组 - 变量
- 通配符
- 占位符
想要使用模式,需要将它与某个值进行比较:如果模式匹配,就可以在代码中使用这个值的相应部分。
18.1.2. match 的分支(Arm)
arm(分支)可以使用模式。它的形式是:
#![allow(unused)]
fn main() {
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
}
match 必须是穷尽的,也就是说你必须覆盖所有可能的情况。
match 中也经常用到 _ 通配符。它会匹配任何东西,并且不会绑定到变量上。它通常用于 match 的最后一个分支,或用于忽略某个值。
如果想看更详细的介绍,可以去 6.3. 控制流运算符-match。
18.1.3. if let 表达式
if let 表达式可以看作是只匹配一种可能性的 match 表达式。
它还可以可选地包含:
else ifelseelse if let
与 match 相比,if let 的缺点是不会检查穷尽性。如果我们省略了最后的 else 块,从而漏掉对某些情况的处理,编译器不会提醒我们可能存在逻辑错误。看个例子:
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, {color}, as the background");
} 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");
}
}
如果用户指定了最喜欢的颜色,就用该颜色作为背景。如果没有指定最喜欢的颜色,并且今天是星期二,则背景颜色为绿色。否则,如果用户把年龄指定为字符串,并且我们能成功把它解析为数字,则颜色为紫色或橙色,具体取决于这个数值。如果这些条件都不适用,则背景颜色为蓝色。
这种条件结构让我们能够支持复杂的需求。使用这里写死的值时,这个例子会打印 Using purple as the background color。
你可以看到,if let 也可以像 match 一样引入遮蔽变量:if let Ok(age) = age 这一行引入了一个新的遮蔽变量 age,其中包含 Ok 里的值。这意味着,在使用上面这种嵌套写法时,我们需要把 if age > 30 放在那个块里:我们想与 30 比较的那个遮蔽后的 age,只在以 { 大括号开始的新作用域内有效。从 Rust 2024 edition 开始,let chains(let 链)也允许把模式匹配和布尔条件写在同一个条件里,例如 if let Ok(age) = age && age > 30;此时 Ok(age) 绑定出的变量可以在同一条 && 链的后续部分中使用。
其余详细内容可见 6.4. 简单的控制流-if let。
18.1.4. while let 条件循环
while let 和 if let 有点相似:只要模式继续匹配,它就允许 while 循环一直运行。
看个例子:
#![allow(unused)]
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{top}");
}
}
这个例子会依次打印 3、2、1。pop 方法会从向量中取出最后一个元素并返回 Some(value)。如果向量为空,pop 返回 None。只要 pop 返回 Some,while 循环就会继续运行其块中的代码。当 pop 返回 None 时,循环停止。我们可以用 while let 把栈中的每个元素依次弹出。
18.1.5. for 循环
for 循环是 Rust 中最常见的循环。在 for 循环中,模式就是紧跟在 for 关键字后面的那个值。
在 for 循环中,直接跟在关键字 for 后面的值是一个模式。例如,在 for x in y 中,x 就是模式。下面的例子演示了如何在 for 循环中使用模式来解构元组:
#![allow(unused)]
fn main() {
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{value} is at index {index}");
}
}
输出:
a is at index 0
b is at index 1
c is at index 2
其余信息可见 3.6. 控制流:循环。
18.1.6. let 语句
let 语句也是模式,其语法是:
#![allow(unused)]
fn main() {
let PATTERN = EXPRESSION;
}
看个例子:
#![allow(unused)]
fn main() {
let (x, y, z) = (1, 2, 3);
}
我们将元组与模式进行匹配。Rust 会把值 (1, 2, 3) 与模式 (x, y, z) 比较,并发现该值与模式匹配,于是把 1 绑定到 x,把 2 绑定到 y,把 3 绑定到 z。你可以把这个元组模式看成其中嵌套了三个单独的变量模式。
18.1.7. 函数参数
函数参数也可以是模式。看个例子:
#![allow(unused)]
fn main() {
fn foo(x: i32) {
// ...
}
}
其中的 x 部分就是一个模式。
正如我们对 let 所做的那样,我们也可以在函数参数中把元组与模式进行匹配。例如:
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({x}, {y})");
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}