Rust match
match 会把一个值和一组模式逐一比较,然后执行第一个对得上的模式所对应的代码。如果你用过 switch 语句,可以把它看作更强大的 switch;就算没用过也没关系,它依然是值得尽早学会的实用写法之一。
基本的 match
Section titled “基本的 match”fn main() { let day = 3;
match day { 1 => println!("星期一"), 2 => println!("星期二"), 3 => println!("星期三"), 4 => println!("星期四"), 5 => println!("星期五"), _ => println!("周末"), }}输出:
星期三_ 是通配模式:它能匹配上面没有列出的任何值。match 要求处理所有可能的值,所以通常会放一个 _ 来兜住“其他所有情况”。
一次匹配多个值
Section titled “一次匹配多个值”可以用 | 把几个值放进同一个分支(arm):
fn main() { let day = 6;
match day { 1 | 2 | 3 | 4 | 5 => println!("工作日"), 6 | 7 => println!("周末"), _ => println!("不是有效的日期"), }}匹配一个范围
Section titled “匹配一个范围”对于数字,可以用 ..= 匹配一整个范围:
fn main() { let score = 85;
match score { 90..=100 => println!("A"), 75..=89 => println!("B"), 60..=74 => println!("C"), _ => println!("F"), }}把 match 当作表达式
Section titled “把 match 当作表达式”和 if 一样,match 也可以产生一个值:
fn main() { let day = 6;
let kind = match day { 1 | 2 | 3 | 4 | 5 => "工作日", 6 | 7 => "周末", _ => "未知", };
println!("{kind}");}匹配文本和字符
Section titled “匹配文本和字符”match 不只能用于数字。它也能匹配字符串 slice(&str)和字符,而且 'a'..='z' 这样的字符范围用起来和数字范围一模一样:
fn main() { let command = "stop"; match command { "start" => println!("正在启动……"), "stop" | "quit" => println!("正在停止。"), _ => println!("未知命令:{command}"), }
let letter = 'k'; let kind = match letter { 'a' | 'e' | 'i' | 'o' | 'u' => "元音字母", 'a'..='z' => "辅音字母", '0'..='9' => "数字", _ => "其他字符", }; println!("'{letter}' 是{kind}");
let name = String::from("Ferris"); match name.as_str() { "Ferris" => println!("你好,小螃蟹!"), _ => println!("你好,{name}!"), }}输出:
正在停止。'k' 是辅音字母你好,小螃蟹!这里有两点值得注意:
- 顺序很重要。
'a'也在'a'..='z'的范围里,但元音分支写在前面,所以元音永远到不了辅音分支。Rust 总是选择第一个对得上的分支。 String需要.as_str()。用引号写的文本,比如"Ferris",是&str。如果直接拿String去和它match,Rust 会报错error[E0308]: mismatched types ... expected String, found &str(类型不匹配:期望String,却得到&str)。调用.as_str()会得到这个String的&str视图,模式就能和它比较了。(两者的区别请看 Rust 字符串一课。)
匹配是区分大小写的:"Stop" 匹配不上 "stop"。如果文本来自用户输入,常用的技巧是匹配 input.to_lowercase().as_str()。
match 与 if/else 的区别
Section titled “match 与 if/else 的区别”对于简单的真/假判断,两者都可以。但一旦可能的情况超过两三种,match 就开始占上风:它比一长串 else if 更好读,而且编译器会检查你是否覆盖了所有情况。对于单个“是/否”条件,if/else 仍然是合适的工具。
1. 每个月有几天
Section titled “1. 每个月有几天”设一个月份 month(2)。把 match 当作表达式,算出这个月有多少天(不考虑闰年),然后打印 2 月有 28 天。提示:1、3、5、7、8、10、12 月有 31 天,用 | 把它们放进同一个分支。
查看答案
fn main() { let month = 2;
let days = match month { 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 4 | 6 | 9 | 11 => 30, 2 => 28, _ => 0, // 不是真实存在的月份 };
println!("{month} 月有 {days} 天");}输出:
2 月有 28 天2. HTTP 状态码
Section titled “2. HTTP 状态码”Web 服务器会用一个数字来回应请求。把 code 转换成简短的含义:200 是 "成功",404 是 "未找到",其他 400–499 是 "客户端错误",500–599 是 "服务器错误",剩下的都是 "未知"。用 503 试一试。提示:要把精确匹配 404 的分支放在 400..=499 范围的上面,否则它永远不会被执行到。
查看答案
fn main() { let code = 503;
let meaning = match code { 200 => "成功", 404 => "未找到", // 必须写在 400..=499 范围之前 400..=499 => "客户端错误", 500..=599 => "服务器错误", _ => "未知", };
println!("{code}:{meaning}");}输出:
503:服务器错误match把一个值和多个模式比较,执行匹配上的那个分支。- 必须处理所有可能的值:用
_作为通配模式兜底。 - 用
|组合多个值,或者用..=匹配一个范围。 &str文本和char值也能匹配;要匹配String,先调用.as_str()。- 和
if一样,match也能当作表达式来产生值。
小测验
1. 在 match 表达式中,_ 模式有什么作用?
2. 如果 match 表达式没有处理所有可能的值,也没有 _ 分支,会怎样?
3. 要在一个分支里匹配多个值(比如 1、2 或 3),怎么写?
得分: 0 / 3
- match 控制流结构 —《Rust 程序设计语言》:官方教程的这一章还演示了如何对枚举和
Option使用match。 - match — 通过例子学 Rust:一个简洁的可运行示例,涵盖具体值、
|、范围和_。 - Range patterns — The Rust Reference(英文):哪些类型和哪些范围写法可以用在模式里。
- Match expressions — The Rust Reference(英文):关于分支、守卫(guard)以及
match如何产生值的精确规则。