Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

18.1 能用到模式(匹配)的地方

18.1.1. 什么是模式

模式(pattern) 是 Rust 里一种特殊的语法,用于匹配复杂类型和简单类型的结构。

将模式与 match 表达式以及其他构造结合使用,可以更好地控制程序的流程。

模式由以下元素的一些组合构成:

  • 字面值
  • 解构的数组、enumstruct 和元组
  • 变量
  • 通配符
  • 占位符

想要使用模式,需要将它与某个值进行比较:如果模式匹配,就可以在代码中使用这个值的相应部分。

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 if
  • else
  • else 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 chainslet 链)也允许把模式匹配和布尔条件写在同一个条件里,例如 if let Ok(age) = age && age > 30;此时 Ok(age) 绑定出的变量可以在同一条 && 链的后续部分中使用。

其余详细内容可见 6.4. 简单的控制流-if let

18.1.4. while let 条件循环

while letif 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 返回 Somewhile 循环就会继续运行其块中的代码。当 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);
}