【Rust自学】6.3. 控制流运算符-match
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
6.3.1. 什么是match
match
允许一个值与一系列模式进行匹配,并执行匹配的模式对应的代码。模式可以是字面值、变量名、通配符等等。
将match
表达式想象为硬币分类机:硬币沿着带有不同大小孔的轨道滑下,每枚硬币都会从它遇到的第一个适合的孔落下。以同样的方式,值会遍历match
中的每个模式,并且在第一个模式中值“适合”,该值落入要在执行期间使用的关联代码块中。
6.3.2. match的应用
来看个例子:编写一个函数,接受一枚未知的美国硬币,并以与计数机类似的方式确定它是哪种硬币并返回其价值(以美分为单位)。
enum Coin {
Penny,// 1美分
Nickel,// 5美分
Dime,// 10美分
Quarter,// 25美分
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
-
match
关键字后跟一个表达式,在本例中是值coin
。这看起来与if
中使用的条件表达式非常相似,但有一个很大的区别:if
的条件需要布尔值,但match
可以是任何类型。本例中的coin
类型是我们在第一行定义的Coin
枚举。 -
然后是花括号,花括号里有4个分支(英文叫arm),每个分支都是由待匹配的模式和它对应的代码来组成的。第一个分支
Coin::Penny => 1,
就使用Coin::Penny
作为它的模式,中间的=>
分隔模式和要运行的代码,这里要运行代码就是值:1,也就是返回1这个值。不同的分支之间使用,
隔开。 -
match
表达式执行时会把match
后的表达式,在这里是coin
,从上到下依次与里面的分支进行比较,如果模式与值匹配,则执行与该模式关联的代码。如果该模式与值不匹配,则继续执行下一个分支。匹配成功的分支所对应的代码表达式会作为整个match
表达式的值进行返回。
比如说match
匹配到5美分,也就是Coin::Nickel
上了,那么整个表达式的值就是5。又因为match
表达式是value_in_cents
这个函数中的最后一个表达式,所以它的值,也就是5,会作为函数的返回值。 -
这里因为每个分支对应的代码都很简单,所以用
=>
就可以了,但如果一个分支对应的是多行代码,就需要用花括号把多行代码括起来。如下例:
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
6.3.3. 绑定值的模式
匹配的分支可以绑定到被匹配对象的部分值,通过这个功能,就可以从枚举类型的变体中提取值。
看个例子:一位朋友正在尝试收集全部 50 个州 25 美分。当我们按硬币类型对零钱进行分类时,我们还会标出与每个25美分相关的州名称(美国州太多了,这里就只写了Alabama和Alaska这两个州)
#[derive(Debug)] // 便于打印调试
enum UsState {
Alabama,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("Lucky penny!");
1
},
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}
fn main() {
let c = Coin::Quarter(UsState::Alaska);
println!("{}", value_in_cents(c));
}
-
让25美分硬币(以下都叫Quarter)所对应的Coin里的变体,也就是
Coin::Quarter
给关联一个数据,它关联的数据就是上面的这个枚举类型UsState
。 -
在
value_in_cents
函数里也需要对Quarter所在的分支稍微修改一下,匹配模式从Coin::Quarter
修改到Coin::Quarter(state)
,意思就是把Coin::Quarter
所关联的值绑定到state
这个变量上,在后面的代码块里面就可以使用state
这个变量,把Coin::Quarter
所关联的值取出来进行使用。
在某些情境下,Coin::Quarter
所关联的值可能用不上,这时候就可以用通配符_
代表不关心里面的内容:Coin::Quarter(_)
-
在
main
函数里先声明了一个c
变量,存的是Coin::Quarter(UsState::Alaska)
。也就是存储了Coin::Quarter
这个变体,然后其关联的值的是UsState::Alaska
这个变体。最后又调用了一下value_in_cents
函数。
看看输出效果:
State quarter from Alaska!
25
6.3.4. 匹配Option<T>
就以上一篇文章最后的代码例来做分析:
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = match y {
Some(value) => x + value, // 如果 y 是 Some,则解包并相加
None => x, // 如果 y 是 None,则返回 x
};
}
- 如果
y
不为None
,就解包,把Some
所关联的值绑定到value
上,返回x + value
的值 - 如果
y
为None
,就只返回x
的值
6.3.5. match匹配必须穷举所有可能
Rust要求match
覆盖到所有的可能性,这样才能保证代码的安全有效。
以上一个代码为基础稍作修改:
fn main() {
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = match y {
Some(value) => x + value,
};
}
输出:
error[E0004]: non-exhaustive patterns: `None` not covered
--> src/main.rs:5:21
|
5 | let sum = match y {
| ^ pattern `None` not covered
|
note: `Option<i8>` defined here
--> /Users/stanyin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:571:1
|
571 | pub enum Option<T> {
| ^^^^^^^^^^^^^^^^^^
...
575 | None,
| ---- not covered
= note: the matched value is of type `Option<i8>`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
6 ~ Some(value) => x + value,
7 ~ None => todo!(),
|
Rust捕捉到了None
这个可能性没有被覆盖的错误,所以报错。把处理None
的分支加上就没问题了。
如果可能性太多或者不想处理其中一些可能性,这个时候就可以使用通配符_
。
6.3.6. 通配符
首先要把想处理的分支照常写上,其他的使用通配符_
代替即可。
看例子:v
是一个u8
类型的变量,判断v
是否是0
use rand::Rng; // 使用外部库
fn main(){
let v: u8 = rand::thread_rng().gen_range(0..=255); // 生成随机数
println!("{}", v);
match v {
0 => println!("zero"),
_ => println!("not zero"),
}
}
u8
有256个数256种可能,使用match
自然是不可能每个数都写一个分支,所以,就可以为0写一个分支,其他的使用通配符_
来代替。
输出:
136
not zero