match 的「穷尽」是 Rust 最实用的安全网之一:编译器替你检查所有情况都处理了。这篇逐条验证它到底查什么、报错信息长什么样、模式有哪些写法,以及一个常被忽视的取舍 —— 用 _ 通配符会让这张安全网在你新增枚举变体时失效。
源码:
1 | enum Dir { North, East, South, West }
2 | fn turn(d: Dir) -> &'static str {
3 | match d {
4 | Dir::North => "向北",
5 | Dir::East => "向东",
6 | }
7 | }
8 | fn main() { println!("{}", turn(Dir::South)); }
编译:失败
error[E0004]: non-exhaustive patterns: `Dir::South` and `Dir::West` not covered
--> <实验目录>/src/u8a.rs:3:11
|
3 | match d {
| ^ patterns `Dir::South` and `Dir::West` not covered
|
note: `Dir` defined here
--> <实验目录>/src/u8a.rs:1:6
|
1 | enum Dir { North, East, South, West }
| ^^^ ----- ---- not covered
| |
| not covered
= note: the matched value is of type `Dir`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
5 ~ Dir::East => "向东",
6 ~ Dir::South | Dir::West => todo!(),
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0004`. 源码:
1 | #[derive(Debug)] enum Msg { Quit, Move { x: i32, y: i32 }, Write(String) }
2 | fn classify(n: u32) -> &'static str {
3 | match n { 0 => "零", 1 | 2 | 3 => "很小", 4..=9 => "个位数", 10..=99 => "两位数", _ => "更大" }
4 | }
5 | fn main() {
6 | for n in [0, 2, 7, 42, 1000] { println!("{n} → {}", classify(n)); }
7 | let m = [Msg::Quit, Msg::Move { x: 3, y: -1 }, Msg::Write("hi".into())];
8 | for x in &m {
9 | let s = match x { Msg::Quit => "退出".to_string(), Msg::Move { x, y: 0 } => format!("水平移动 {x}"), Msg::Move { x, y } if *y < 0 => format!("向下 {y},横向 {x}"), Msg::Move { .. } => "其它移动".to_string(), Msg::Write(t) => format!("写入 {t}") };
10 | println!("{:?} → {s}", x);
11 | }
12 | let age = 25;
13 | match age { n @ 18..=64 => println!("成年人,年龄 {n}"), n => println!("其它 {n}") }
14 | match (3, -3) { (x, y) if x + y == 0 => println!("元组:和为零"), (x, _) if x % 2 == 1 => println!("x 是奇数"), _ => println!("都不是") }
15 | }
编译:成功
运行(退出码 0):
0 → 零
2 → 很小
7 → 个位数
42 → 两位数
1000 → 更大
Quit → 退出
Move { x: 3, y: -1 } → 向下 -1,横向 3
Write("hi") → 写入 hi
成年人,年龄 25
元组:和为零更紧凑的单模式写法见if let 与 let else。
(1) 显式列全每个变体的 match,Level 只有 Low、High:
编译:成功
运行(退出码 0):
2
(2) 给 Level 新增变体 Critical,显式 match 一行不改:
编译:失败
error[E0004]: non-exhaustive patterns: `Level::Critical` not covered
--> <实验目录>/src/u8d.rs:2:30
|
2 | fn f(l: Level) -> u8 { match l { Level::Low => 1, Level::High => 2 } }
| ^ pattern `Level::Critical` not covered
|
note: `Level` defined here
--> <实验目录>/src/u8d.rs:1:23
|
1 | #[derive(Debug)] enum Level { Low, High, Critical }
| ^^^^^ -------- not covered
= note: the matched value is of type `Level`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
2 | fn f(l: Level) -> u8 { match l { Level::Low => 1, Level::High => 2, Level::Critical => todo!() } }
| ++++++++++++++++++++++++++++
error: aborting due to 1 previous error
…(共 20 行)
(3) 同样新增 Critical,但 match 里用了 _ 通配:
编译:成功
运行(退出码 0):
2对「别人的、会增长的」枚举(标了 #[non_exhaustive] 的)你反而必须写 _;这个属性我没有在这里验证。
源码:
1 | fn main() {
2 | let n = 3;
3 | let s = match n { 1 => "one", 2 => 2, _ => "many" };
4 | println!("{}", s);
5 | }
编译:失败
error[E0308]: `match` arms have incompatible types
--> <实验目录>/src/u8e.rs:3:40
|
3 | let s = match n { 1 => "one", 2 => 2, _ => "many" };
| ------- ----- ^ expected `&str`, found integer
| | |
| | this is found to be of type `&str`
| `match` arms have incompatible types
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.
源码:
1 | fn main() {
2 | let x: u8 = std::env::args().count() as u8;
3 | let s = match x { 0..=254 => "不是 255" };
4 | println!("{s}");
5 | }
编译:失败
error[E0004]: non-exhaustive patterns: `u8::MAX` not covered
--> <实验目录>/src/u8f.rs:3:19
|
3 | let s = match x { 0..=254 => "不是 255" };
| ^ pattern `u8::MAX` not covered
|
= note: the matched value is of type `u8`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
3 | let s = match x { 0..=254 => "不是 255", u8::MAX => todo!() };
| ++++++++++++++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0004`.