跳转到内容

Rust match

match 会把一个值和一组模式逐一比较,然后执行第一个对得上的模式所对应的代码。如果你用过 switch 语句,可以把它看作更强大的 switch;就算没用过也没关系,它依然是值得尽早学会的实用写法之一。

fn main() {
let day = 3;
match day {
1 => println!("星期一"),
2 => println!("星期二"),
3 => println!("星期三"),
4 => println!("星期四"),
5 => println!("星期五"),
_ => println!("周末"),
}
}

输出:

星期三
▶ 动手试一试

_ 是通配模式:它能匹配上面没有列出的任何值。match 要求处理所有可能的值,所以通常会放一个 _ 来兜住“其他所有情况”。

可以用 | 把几个值放进同一个分支(arm):

fn main() {
let day = 6;
match day {
1 | 2 | 3 | 4 | 5 => println!("工作日"),
6 | 7 => println!("周末"),
_ => println!("不是有效的日期"),
}
}
▶ 动手试一试

对于数字,可以用 ..= 匹配一整个范围:

fn main() {
let score = 85;
match score {
90..=100 => println!("A"),
75..=89 => println!("B"),
60..=74 => println!("C"),
_ => println!("F"),
}
}
▶ 动手试一试

和 if 一样,match 也可以产生一个值:

fn main() {
let day = 6;
let kind = match day {
1 | 2 | 3 | 4 | 5 => "工作日",
6 | 7 => "周末",
_ => "未知",
};
println!("{kind}");
}
▶ 动手试一试

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 就开始占上风:它比一长串 else if 更好读,而且编译器会检查你是否覆盖了所有情况。对于单个“是/否”条件,if/else 仍然是合适的工具。

设一个月份 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 天
▶ 动手试一试

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