可证伪性:某个模式有无可能匹配失败
Refutability: Whether a Pattern Might Fail to Match
模式有两种形式:可证伪与不可证伪的。将匹配所传递的任何可能值模式,即为 不可证伪的,irrefuatable。一个示例即为 let x = 5;
语句中的 x
;由于 x
会匹配任何值,而因此必定不会匹配失败。那些对某些可能的值,会匹配失败的模式,便是 可证伪的,refutable。这样的一个示例,便是表达式 if let Some(x) = a_value
中的 Some(x)
,因为若 a_value
中的值为 None
而非 Some
时,这个 Some(x)
模式就将不匹配。
函数参数、let
语句及 for
循环,就只能接受不可证伪的模式,这是由于这些情况下,当值不匹配时,程序便无法执行任何有意义的事情。if let
与 while let
表达式,接受可证伪与不可证伪的模式,但由于根据定义,他们被计划来处理可能的匹配失败:某个条件的功能,便在于其根据匹配成功或失败,而区别执行的能力,因此编译器会就不可证伪模式发出告警。
一般来说,咱们无须担心可证伪与不可证伪模式的区别;但是,咱们确实需要熟悉可证伪性的概念,这样咱们在看到报错消息时,就可以予以响应。在这些情形下,咱们将需要根据代码所预期的行为,而要么修改模式,或者修改该模式下用到的那个结构。
接下来就看看,当咱们在 Rust 要求使用不可证伪模式的地方,尝试使用某种可证伪模式,及反过来 Rust 要求使用可证伪模式,而尝试使用不可证伪模式时,会发生什么。下面清单 18-8 给出了一个 let
语句,不过咱们指定了一个可证伪的 Some(x)
模式。正如你会期待的那样,此代码将不会编译。
#![allow(unused)] fn main() { let Some(x) = some_option_value; }
清单 18-:在 let
下使用可证伪模式的尝试*
若 some_option_value
是个 None
值,他就会与模式 Some(x)
匹配失败,意味着该模式为可证伪的。但是,由于此处没有可处理 None
值的有效代码,因此该 let
表达式就只能接收某个不可证伪的模式。在编译时,Rust 将抱怨说咱们曾于要求不可证伪模式的某处,使用了可证伪模式:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding: `None` not covered
--> 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/ch18-02-refutability.html
note: `Option<i32>` defined here
= note: the matched value is of type `Option<i32>`
help: you might want to use `if let` to ignore the variant that isn't matched
|
3 | let x = if let Some(x) = some_option_value { x } else { todo!() };
| ++++++++++ ++++++++++++++++++++++
For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` due to previous error
由于咱们不曾以 Some(x)
涵盖(且无法涵盖到!)所以有效值,Rust 便理所当然地产生了一个编译器报错。
而当咱们在需要不可证伪模式处,有着某个可证伪模式时,咱们可通过修改用到该模式的代码修复之:与其使用 let
,咱们可以使用 if let
。随后在该模式不匹配时,该代码就仅仅会跳过位于那花括号中代码,从而给了其有效继续的一种方式。下面清单 18-9 给出了修复清单 18-8 中代码的方式。
#![allow(unused)] fn main() { if let Some(x) = some_option_value { println! ("{}", x); } }
清单 18-9:使用 if let
与带有可证伪模式代码块,而非 let
咱们就已给了代码一条出路了!这段代码是完全有效的,尽管其意味着咱们在不接收到报错下,无法使用某个不可证伪模式。而在咱们给到 if let
某个将始终匹配的模式时,如下清单 18-10 中所示,编译器就会给出一条告警。
#![allow(unused)] fn main() { if let x = 5 { println! ("{}", x); } }
清单 18-10:在 if let
下使用不可证伪模式的尝试
Rust 会抱怨,以某个不可证伪模式使用 if let
没有意义:
$ cargo run lennyp@vm-manjaro
Compiling refutable_demo v0.1.0 (/home/lennyp/rust-lang/refutable_demo)
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: `refutable_demo` (bin "refutable_demo") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
Running `target/debug/refutable_demo`
5
由于这个原因,除了应以一个不可证伪模式匹配全部剩余值的最后支臂外,其他那些匹配支臂,就必须使用可证伪模式。Rust 允许咱们在仅有一个支臂的 match
中,使用不可证伪模式,但这种语法不是特别有用,并可以一个更简单的 let
语句替换。
现在,咱们就知道了哪些地方要使用模式,以及可证伪与不可证伪模式的区别,下面就来介绍所有可用于创建模式的语法。