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

可反驳性:模式是否可能匹配失败

Refutability: Whether a Pattern Might Fail to Match

模式有两种形式:可反驳的和不可反驳的。对于任何传递的可能值都能匹配的模式是不可反驳的 (irrefutable)。例如,语句 let x = 5; 中的 x 就是一个例子,因为 x 可以匹配任何内容,因此永远不会匹配失败。对于某些可能值可能会匹配失败的模式是可反驳的 (refutable)。例如,表达式 if let Some(x) = a_value 中的 Some(x) 就是一个例子,因为如果 a_value 变量中的值是 None 而不是 Some,那么 Some(x) 模式将无法匹配。

Patterns come in two forms: refutable and irrefutable. Patterns that will match for any possible value passed are irrefutable. An example would be x in the statement let x = 5; because x matches anything and therefore cannot fail to match. Patterns that can fail to match for some possible value are refutable. An example would be Some(x) in the expression if let Some(x) = a_value because if the value in the a_value variable is None rather than Some, the Some(x) pattern will not match.

函数参数、let 语句和 for 循环只能接受不可反驳的模式,因为当值不匹配时,程序无法执行任何有意义的操作。if letwhile let 表达式以及 let...else 语句接受可反驳和不可反驳的模式,但编译器会对不可反驳的模式发出警告,因为根据定义,它们旨在处理可能的失败:条件句的功能在于它能够根据成功或失败而表现出不同的行为。

Function parameters, let statements, and for loops can only accept irrefutable patterns because the program cannot do anything meaningful when values don’t match. The if let and while let expressions and the let...else statement accept refutable and irrefutable patterns, but the compiler warns against irrefutable patterns because, by definition, they’re intended to handle possible failure: The functionality of a conditional is in its ability to perform differently depending on success or failure.

通常情况下,你不必担心可反驳和不可反驳模式之间的区别;但是,你需要熟悉可反驳性的概念,以便在错误消息中看到它时做出响应。在这些情况下,你需要根据代码的预期行为,更改模式或使用该模式的构造。

In general, you shouldn’t have to worry about the distinction between refutable and irrefutable patterns; however, you do need to be familiar with the concept of refutability so that you can respond when you see it in an error message. In those cases, you’ll need to change either the pattern or the construct you’re using the pattern with, depending on the intended behavior of the code.

让我们看一个例子,看看当我们尝试在 Rust 要求不可反驳模式的地方使用可反驳模式时会发生什么,反之亦然。示例 19-8 显示了一个 let 语句,但对于模式,我们指定了 Some(x),这是一个可反驳模式。正如你可能预料的那样,这段代码将无法编译。

Let’s look at an example of what happens when we try to use a refutable pattern where Rust requires an irrefutable pattern and vice versa. Listing 19-8 shows a let statement, but for the pattern, we’ve specified Some(x), a refutable pattern. As you might expect, this code will not compile.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}

如果 some_option_value 是一个 None 值,它将无法匹配模式 Some(x),这意味着该模式是可反驳的。然而,let 语句只能接受不可反驳模式,因为代码无法对 None 值执行任何有效的操作。在编译时,Rust 会抱怨我们在需要不可反驳模式的地方尝试使用了可反驳模式:

If some_option_value were a None value, it would fail to match the pattern Some(x), meaning the pattern is refutable. However, the let statement can only accept an irrefutable pattern because there is nothing valid the code can do with a None value. At compile time, Rust will complain that we’ve tried to use a refutable pattern where an irrefutable pattern is required:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
 --> src/main.rs:3:9
  |
3 |     let Some(x) = some_option_value;
  |         ^^^^^^^ pattern `None` not covered
  |
  = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
  = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html
  = note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
  |
3 |     let Some(x) = some_option_value else { todo!() };
  |                                     ++++++++++++++++

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

因为我们没有(也无法!)用模式 Some(x) 覆盖每一个有效的值,Rust 理所当然地产生了一个编译器错误。

Because we didn’t cover (and couldn’t cover!) every valid value with the pattern Some(x), Rust rightfully produces a compiler error.

如果我们在需要不可反驳模式的地方使用了可反驳模式,我们可以通过更改使用该模式的代码来修复它:我们可以使用 let...else 代替 let。这样,如果模式不匹配,花括号中的代码将处理该值。示例 19-9 展示了如何修复示例 19-8 中的代码。

If we have a refutable pattern where an irrefutable pattern is needed, we can fix it by changing the code that uses the pattern: Instead of using let, we can use let...else. Then, if the pattern doesn’t match, the code in the curly brackets will handle the value. Listing 19-9 shows how to fix the code in Listing 19-8.

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}

我们给代码提供了一个出路!这段代码是完全有效的,尽管这意味着我们在不收到警告的情况下不能使用不可反驳模式。如果我们给 let...else 一个总能匹配的模式,例如 x,如示例 19-10 所示,编译器将给出警告。

We’ve given the code an out! This code is perfectly valid, although it means we cannot use an irrefutable pattern without receiving a warning. If we give let...else a pattern that will always match, such as x, as shown in Listing 19-10, the compiler will give a warning.

fn main() {
    let x = 5 else {
        return;
    };
}

Rust 抱怨在不可反驳模式下使用 let...else 是没有意义的:

Rust complains that it doesn’t make sense to use let...else with an irrefutable pattern:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `let...else` pattern
 --> src/main.rs:2:5
  |
2 |     let x = 5 else {
  |     ^^^^^^^^^
  |
  = note: this pattern will always match, so the `else` clause is useless
  = help: consider removing the `else` clause
  = note: `#[warn(irrefutable_let_patterns)]` on by default

warning: `patterns` (bin "patterns") generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/patterns`

出于这个原因,match 分支必须使用可反驳模式,除了最后一个分支,它应该使用不可反驳模式匹配任何剩余的值。Rust 允许我们在只有一个分支的 match 中使用不可反驳模式,但这种语法并不是特别有用,可以用更简单的 let 语句代替。

For this reason, match arms must use refutable patterns, except for the last arm, which should match any remaining values with an irrefutable pattern. Rust allows us to use an irrefutable pattern in a match with only one arm, but this syntax isn’t particularly useful and could be replaced with a simpler let statement.

现在你已经知道了模式可以在哪里使用以及可反驳和不可反驳模式之间的区别,让我们介绍一下我们可以用来创建模式的所有语法。

Now that you know where to use patterns and the difference between refutable and irrefutable patterns, let’s cover all the syntax we can use to create patterns.