可以使用所有 Places 模式

Patterns 在 Rust 中的许多地方都出现了,你已经经常使用它们了 不知不觉中!本节讨论模式所在的所有位置 有效。

火柴武器

正如第 6 章所讨论的,我们在表达式的臂中使用模式。 正式地,表达式被定义为关键字 ,一个值为 match on,以及一个或多个由 pattern 和 表达式,如果值与该 Arm 的模式匹配,则运行,如下所示:matchmatchmatch

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}

例如,下面是示例 6-5 中匹配变量中的值的表达式:matchOption<i32>x

match x {
    None => None,
    Some(i) => Some(i + 1),
}

此表达式中的模式是 和 上的 每个箭头的左侧。matchNoneSome(i)

表达式的一个要求是它们需要详尽无遗 表示表达式中值的所有可能性都必须 被考虑。确保您已涵盖所有可能性的一种方法是拥有 最后一个分支的 catchAll 模式:例如,与任何 value 永远不会失败,因此涵盖了所有剩余的情况。matchmatch

特定模式将匹配任何内容,但它永远不会绑定到 variable,因此它经常用于 last match 臂。模式可以是 例如,当您想要忽略任何未指定的值时很有用。我们将 在“忽略 a 中的值”中更详细地介绍了该模式 Pattern“ 部分 章。___

条件 if let 表达式

在第 6 章中,我们讨论了如何主要将表达式用作 shorter 编写仅匹配一个 case 的 a 的等效方法。 (可选)如果 中的模式不匹配。if letmatchif letelseif let

示例 18-1 显示了也可以混合和匹配 、 和 表达式。这样做为我们提供了比表达式更大的灵活性,在表达式中,我们只能表示一个值来与 模式。此外,Rust 不要求一系列 , , 臂中的条件彼此相关。if letelse ifelse if letmatchif letelse ifelse if let

示例 18-1 中的代码决定了背景的颜色 针对多个条件的一系列检查。在此示例中,我们创建了 具有实际程序可能从用户那里接收的硬编码值的变量 输入。

文件名: src/main.rs

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");
    }
}

示例 18-1:混合 if letelse ifelse if let等等

如果用户指定了最喜欢的颜色,则该颜色将用作背景。 如果未指定收藏夹颜色,并且今天是星期二,则背景色为 绿。否则,如果用户将他们的年龄指定为字符串,并且我们可以解析 它成功作为一个数字,颜色是紫色或橙色,具体取决于 数字的值。如果这些条件都不适用,则背景 颜色是蓝色。

这种条件结构使我们能够支持复杂的需求。使用 hardcoded values 的 hardcoded 值,此示例将打印 .Using purple as the background color

你可以看到,它也可以以同样的方式引入阴影变量 that arms can: 该系列引入了一个新的 shadowed 变量,其中包含 Variant 内的值。这 意味着我们需要将条件放在该块中:我们不能 将这两个条件合并到 中。这 shadowed 的 30 在新的范围开始之前无效 带大括号。if letmatchif let Ok(age) = ageageOkif age > 30if let Ok(age) = age && age > 30age

using 表达式的缺点是编译器不会检查 for exhaustiveness,而 with expressions 则如此。如果我们省略了 last 块,因此错过了处理某些情况,编译器将 不会提醒我们可能的 logic 错误。if letmatchelse

while let条件循环

在构造上与 类似,条件循环允许循环在模式继续匹配时运行。在列表中 18-2 我们编写一个循环,使用向量作为堆栈并打印 值,其推送顺序与它们被推送的顺序相反。if letwhile letwhilewhile let

fn main() {
    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
        println!("{top}");
    }
}

示例 18-2:使用 while let 循环打印值 只要 stack.pop() 返回 Some

此示例打印 3、2 和 1。该方法采用最后一个元素 从 vector 中传出并返回 。如果向量为空,则返回 。循环继续运行其块中的代码,因为 只要返回 .当返回 时,循环停止。我们可以 用于将每个元素从堆栈中弹出。popSome(value)popNonewhilepopSomepopNonewhile let

循环

在循环中,关键字后面的值为 模式。例如,in the is the 模式。示例 18-3 演示如何在循环中使用模式来解构或断开 apart,一个 Tuples 作为循环的一部分。forforfor x in yxforfor

fn main() {
    let v = vec!['a', 'b', 'c'];

    for (index, value) in v.iter().enumerate() {
        println!("{value} is at index {index}");
    }
}

示例 18-3:在 for 循环中使用模式 to 解构元组

示例 18-3 中的代码将打印以下内容:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
     Running `target/debug/patterns`
a is at index 0
b is at index 1
c is at index 2

我们使用该方法调整迭代器,使其生成一个值 该值的索引,放置在 Tuples 中。生成的第一个值是 元。当此值与 pattern 匹配时,将是 和 将是 ,打印 输出。enumerate(0, 'a')(index, value)index0value'a'

语句

在本章之前,我们只明确讨论了使用 和 的模式,但实际上,我们也在其他地方使用了模式。 包括在声明中。例如,考虑一下这个简单的 变量赋值 :matchif letletlet

#![allow(unused)]
fn main() {
let x = 5;
}

每次你使用这样的语句时,你都在使用 patterns, 尽管您可能没有意识到!更正式地说,声明看起来 喜欢这个:letlet

let PATTERN = EXPRESSION;

在语句中,例如在 slot 中使用变量名称, 变量名称只是模式的一种特别简单的形式。Rust 比较 表达式,并为其找到的任何名称分配。所以在这个例子中,是一个模式,表示 “bind what matches here to 变量 .因为 name 是整个模式,所以这个模式 实际上意味着 “将所有内容绑定到变量 ,无论值是什么”。let x = 5;PATTERNlet x = 5;xxxx

要更清楚地看到 的模式匹配方面,请考虑 清单 18-4 中,它使用 pattern with 来解构 Tuples。letlet

fn main() {
    let (x, y, z) = (1, 2, 3);
}

示例 18-4:使用模式解构元组和 一次创建三个变量

在这里,我们将元组与模式进行匹配。Rust 将该值与模式进行比较,并发现该值与模式匹配,因此 Rust 绑定到 、 和 。你可以考虑这个元组 pattern 嵌套三个单独的可变 pattern。(1, 2, 3)(x, y, z)1x2y3z

如果模式中的元素数与元素数不匹配 在 Tuples 中,整体类型不匹配,我们将收到 Compiler 错误。为 例如,示例 18-5 展示了一个尝试用 3 个 元素转换为两个变量,这不起作用。

fn main() {
    let (x, y) = (1, 2, 3);
}

示例 18-5:错误地构造一个 变量与元组中的元素数不匹配

尝试编译此代码会导致以下类型错误:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     let (x, y) = (1, 2, 3);
  |         ^^^^^^   --------- this expression has type `({integer}, {integer}, {integer})`
  |         |
  |         expected a tuple with 3 elements, found one with 2 elements
  |
  = note: expected tuple `({integer}, {integer}, {integer})`
             found tuple `(_, _)`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error

为了修复这个错误,我们可以使用 or 忽略元组中的一个或多个值,正如你将在“忽略 Pattern“部分。如果问题 是我们在 pattern 中有太多的变量,解决方案是让 类型通过删除变量进行匹配,使变量的数量等于数字 元组中的元素。_..

功能参数

函数参数也可以是模式。示例 18-6 中的代码,其中 声明一个名为 的函数,该函数接受一个名为 类型的参数 ,现在看起来应该很熟悉。fooxi32

fn foo(x: i32) {
    // code goes here
}

fn main() {}

示例 18-6:函数签名使用 参数

部分是花纹!就像我们对 所做的那样,我们可以在 函数的参数传递给模式。示例 18-7 将值拆分为一个 Tuples 当我们将其传递给函数时。xlet

文件名: src/main.rs

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({x}, {y})");
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}

示例 18-7:一个带有解构参数的函数 一个元组

此代码打印 .值与 pattern ,值 也是 ,值 是 。Current location: (3, 5)&(3, 5)&(x, y)x3y5

我们也可以像 function parameter lists 的 API API 中,因为闭包类似于函数,因为 在第 13 章中讨论。

此时,您已经看到了几种使用 patterns 的方法,但 patterns 没有 在我们可以使用它们的每个地方都以相同的方式工作。在某些地方,模式必须 无可辩驳;在其他情况下,它们可以被反驳。我们将讨论 接下来是这两个概念。

本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准