键盘快捷键

按 ← 或 → 在章节之间导航

按 S 或 / 在书中搜索

按 ? 显示此帮助

按 Esc 隐藏此帮助

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

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

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

一般来说,你不必担心可反驳模式与无可反驳模式之间的区别; 但是,你确实需要熟悉可反驳性的概念,以便在错误 信息中看到它时能做出反应。在那些情况下,你需要 根据代码的预期行为,要么修改模式,要么修改使用模式 的语法结构。

让我们看一个例子:当我们试图在 Rust 要求无可反驳模式的地方 使用可反驳模式(以及反过来)时会发生什么。示例 19-8 展示了一个 let 语句,但我们指定的模式 Some(x) 是一个可反驳 模式。如你所料,这段代码不会编译。

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value;
}
示例 19-8:试图在 let 中使用可反驳模式

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

$ 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 理所当然地产生了编译器错误。

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

fn main() {
    let some_option_value: Option<i32> = None;
    let Some(x) = some_option_value else {
        return;
    };
}
示例 19-9:使用 let...else 以及一个处理可反驳模式的代码块来替代 let

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

fn main() {
    let x = 5 else {
        return;
    };
}
示例 19-10:试图在 let...else 中使用无可反驳模式

Rust 抱怨说,在无可反驳模式下使用 let...else 没有意义, 因为 else 永远不会被执行:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
warning: unreachable `else` clause
 --> src/main.rs:2:15
  |
2 |     let x = 5 else {
  |     --------- ^^^^
  |     |
  |     assigning to binding pattern will always succeed
  |
  = note: this pattern always matches, so the else clause is unreachable
  = 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 语句替代。

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