18.2 可辩驳性:模式是否会无法匹配
18.2.1. 模式的两种形式
模式有两种形式:
- 可辩驳的(refutable),意味着它们可能匹配失败
- 无可辩驳的(irrefutable),意味着它们不会失败;你可以把它理解为无论怎么写都会成功的模式
能够匹配任意可能传入值的模式,就是无可辩驳的。例如:
#![allow(unused)]
fn main() {
let x = 5;
}
这个语句不可能失败,因为 x 能匹配表达式右侧所有可能的值。
无法匹配某些可能值的模式,就是可辩驳的。例如:
#![allow(unused)]
fn main() {
if let Some(x) = a_value
}
如果右侧的值是 None,模式就会匹配失败。
函数参数、let 语句和 for 循环只接受无可辩驳模式。例如:
#![allow(unused)]
fn main() {
let a: Option<i32> = Some(5);
let Some(x) = a;
}
Some(x) = a 是可辩驳的,因为也有可能是 None,但 let 语句只接受无可辩驳模式,所以编译器会报错。那该怎么改呢?可以使用 if let,也可以使用 let...else 来处理匹配失败的情况:
#![allow(unused)]
fn main() {
let a: Option<i32> = Some(5);
if let Some(x) = a {
// ...
}
}
#![allow(unused)]
fn main() {
let a: Option<i32> = Some(5);
let Some(x) = a else {
return;
};
}
if let、while let 和 let...else 同时支持可辩驳和无可辩驳模式。实际上,如果你在 if let、while let 或 let...else 中使用无可辩驳模式,编译器会发出警告,因为从概念上讲这里本来就存在失败的可能。例如:
#![allow(unused)]
fn main() {
if let x = 5 {
println!("{x}");
};
}
输出:
$ cargo run
Compiling patterns v0.1.0 (/tmp/ch18-refresh/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` profile [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/patterns`
5
编译器会警告 “irrefutable if let pattern”。那是因为:在本应用于可辩驳模式的上下文中使用无可辩驳模式,是没有意义的。
基于这些概念,再想想 match 表达式的分支:除了最后一个分支以外,其他分支都应该是可辩驳的;而最后一个分支应该是无可辩驳的,因为它需要匹配所有剩余情况。