可反驳性:模式是否可能匹配失败
模式有两种形式:可反驳的和不可反驳的。 对于传递的任何可能值都能匹配的模式是*不可反驳的*。例如语句 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
表达式可以接受可反驳的和不可反驳的模式,但编译器会针对不可反驳的模式发出警告,因为根据定义,它们旨在处理可能的失败:条件语句的功能在于它能够根据成功或失败执行不同的操作。
通常情况下,您不必担心可反驳模式和不可反驳模式之间的区别;但是,您需要熟悉可反驳性的概念,以便在错误消息中看到它时能够做出反应。 在这些情况下,您需要根据代码的预期行为更改模式或使用该模式的结构。
让我们来看一个例子,看看当我们尝试在 Rust 需要不可反驳模式的地方使用可反驳模式,以及反过来使用时会发生什么。 清单 18-8 展示了一个 let
语句,但我们为其指定的模式是 Some(x)
,这是一个可反驳的模式。 正如您可能预料的那样,这段代码无法编译。
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
如果 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.net.cn/book/ch18-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
,而是使用 if let
。 这样,如果模式不匹配,代码将跳过大括号中的代码,从而使其能够有效地继续执行。 清单 18-9 展示了如何修复清单 18-8 中的代码。
fn main() { let some_option_value: Option<i32> = None; if let Some(x) = some_option_value { println!("{x}"); } }
我们为代码提供了一种出路! 这段代码现在完全有效。 但是,如果我们给 if let
一个不可反驳的模式(一个总是匹配的模式),例如 x
,如清单 18-10 所示,编译器会发出警告。
fn main() { if let x = 5 { println!("{x}"); }; }
Rust 抱怨说,对不可反驳的模式使用 if let
没有意义。
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: `patterns` (bin "patterns") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
出于这个原因,匹配分支必须使用可反驳模式,最后一个分支除外,它应该使用不可反驳模式匹配任何剩余的值。Rust 允许我们在只有一个分支的 match
中使用不可反驳模式,但这种语法并不是特别有用,可以用更简单的 let
语句代替。
现在您已经知道了在哪里使用模式以及可反驳模式和不可反驳模式之间的区别,让我们来介绍一下可用于创建模式的所有语法。