Refutability(可反驳性): 模式是否会匹配失效

匹配模式有两种形式: refutable(可反驳)和irrefutable(不可反驳). 对任意可能的值进行匹配都不会失效的模式被称为是irrefutable(不可反驳)的, 而对某些可能的值进行匹配会失效的模式被称为是refutable(可反驳)的. let语句、 函数参数和for循环被约束为只接受irrefutable模式, 因为如果模式匹配失效程序就不会正确运行. if letwhile let表达式被约束为只接受refutable模式, 因为它们需要处理可能存在的匹配失效的情况, 并且如果模式匹配永不失效, 那它们就派不上用场了.

通常, 你不用关心refutableirrefutable模式的区别, 当你看见它出现在了错误消息中时, 你只要了解可反驳性(refutability)的概念即可. 如果你得到一个涉及到可反驳性概念的错误消息, 根据你的代码行为的意图, 你只需改变匹配模式或者是改变你构造模式的方法即可.

让我们来看几个例子. 在本章的前面部分, 我们提到let x = 5;. 这里x就是一个我们被允许使用irrefutable的模式: 因为它不可能匹配失效. 相反, 如果用let来匹配一个枚举的变体, 比如像例18-7中列出的那样从Option<T>枚举中只匹配Some<T>这个值:

let Some(x) = some_option_value;

例18-7: 试试用一个有letrefutable模式

如果some_option_value的值是None, some_option_value将不会匹配模式Some(x). 模式Some(x)是可反驳的(refutable), 因为存在一个使它匹配失效的值. 如果some_option_value的值是None, 那么let语句就不会产生任何效果. 因此Rust会在编译时会报期望irrefutable模式但是却得到了一个refutable模式的错误:

error[E0005]: refutable pattern in local binding: `None` not covered
 --> <anon>:3:5
  |
3 | let Some(x) = some_option_value;
  |     ^^^^^^^ pattern `None` not covered

因为我们没有(也不能)覆盖到模式Some(x)的每一个可能的值, 所以Rust会报错.

如果我们采用refutable模式, 使用if let而不是let. 这样当模式不匹配时, 在花括号中的代码将不执行, 这段代码只有在值匹配模式的时候才会执行, 也只在此时才有意义. 例18-8显示了如何修正在例18-7中用Some(x)来匹配some_option_value的代码. 因为这个例子使用了if let, 因此使用refutable模式的Some(x)就没问题了:


# #![allow(unused_variables)]
#fn main() {
# let some_option_value: Option<i32> = None;
if let Some(x) = some_option_value {
    println!("{}", x);
}
#}

例18-8: 使用if let和一个有refutable模式的代码块来代替let

此外, 如果我们给if let一个绝对会匹配的irrefutable模式, 比如在例18-9中显示的x:

if let x = 5 {
    println!("{}", x);
};

例18-9: 尝试把一个irrefutable模式用到if let

Rust将会抱怨把if let和一个irrefutable模式一起使用没有意义:

error[E0162]: irrefutable if-let pattern
 --> <anon>:2:8
  |
2 | if let x = 5 {
  |        ^ irrefutable pattern

一般来说, 多数匹配使用refutable模式, 除非是那种可以匹配任意值的情况使用irrefutable模式. match操作符中如果只有一个irrefutable模式分支也没有什么问题, 但这就没什么特别的用处, 此时可以用一个更简单的let语句来替换. 不管是把表达式关联到let语句亦或是关联到只有一个irrefutable模式分支的match操作, 代码都肯定会运行, 如果它们的表达式一样的话最终的结果也相同.

目前我们已经讨论了所有可以使用模式的地方, 也介绍了refutable模式和irrefutable模式的不同, 下面让我们一起去把可以用来创建模式的语法过目一遍吧.