【Fitz】Rust 模式匹配
说明:本文主要是Rust语言圣经相关章节的学习笔记,大部分与其内容相同,欢迎阅读原文。
match 和 if let
Rust 中,模式匹配最常用的就是 match 和 if let。
在使用 match 匹配枚举类型时,match 的匹配必须穷举出所有可能,因此可以用 _ 来代表未列出的所有可能性。match 的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同。使用 X | Y 代表该分支可以匹配 X 也可以匹配 Y,二者满足一个即可。match 跟其他语言中的 switch 非常像,_ 类似于 switch 中的 default。
match 匹配
match 的通用使用形式:
match target {
模式1 => 表达式1,
模式2 => {
语句1;
语句2;
表达式2
},
_ => 表达式3
}
模式匹配,即将模式与 target 进行匹配,但模式匹配不仅仅局限于 match。
match 允许将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码。
match 后跟的表达式可以是任意类型,只要能跟后面的分支中的模式匹配起来即可。match 的分支包含两个部分:一个模式和针对该模式的处理代码。匹配到的模式之后的代码将被执行,每个分支相关联的代码是一个表达式,表达式的值将作为整个 match 表达式的返回值,有多行代码时需要使用 {}。
使用 match 表达式赋值
match 本身是一个表达式,能用来给变量赋值,如:
let ip_str = match ip1 {
IpAddr::Ipv4 => "127.0.0.1",
_ => "::1",
};
模式绑定
模式匹配的另外一个重要功能是从模式中取出绑定的值。通过 match 可以从同一个枚举类型下的不同模式中取出不同的值。
穷尽匹配
match 的匹配必须穷尽所有情况,如果忽略了某些情况没有处理的话编译器会直接报错 pattern 'XXX' not coverd。
_ 通配符
当不想在匹配时列出所有值的时候,可以使用 Rust 提供的 _ 通配符。通过将 _ 放置于其他分支后,_ 将会匹配所有遗漏的值。() 可以返回单元类型,与所有分支返回值类型相同。当使用 _ => () 来处理时,什么也不会发生。
if let 匹配
有时会遇到只有一个模式的值需要被处理,其他值直接忽略的场景,这时用 match 就会很麻烦,可以直接使用 if let 来实现。
当只需要匹配一个条件,且直接忽略其他条件时就用 if let,否则都用 match。
let v= Some(3u8);
if let Some(3) = v {
println!("three");
}
matches! 宏
Rust 标准库中提供了 matches! 宏,可以将一个表达式跟模式进行匹配,然后返回匹配的结果 true 或 false。
例如有动态数组,里面存有枚举:
enum MyEnum {
Foo,
Bar,
}
fn main() {
let v = vec![MyEnum::Foo, MyEnum::Bar, MyEnum::Foo];
}
如果想对 v 进行过滤,只保留 MyEnum::Foo 的元素,可以使用 matches!:
let v2 = v.iter().filter(|x| matches!(x, MyEnum::Foo)); //迭代器适配器,返回一个新的迭代器
变量覆盖
无论是 match 还是 if let,都可以在模式匹配时覆盖掉老的值,绑定新的值,示例如下。需要注意,match 中的变量覆盖不是那么容易看出,需要小心。
// if let 变量覆盖
let age = Some(30);
println!("在匹配前,age是{:?}",age);
if let Some(age) = age {
println!("匹配出来的age是{}",age);
}
println!("在匹配后,age是{:?}",age);
// match 变量覆盖
let age = Some(30);
println!("在匹配前,age是{:?}",age);
match age {
Some(age) => println!("匹配出来的age是{}",age),
_ => ()
}
println!("在匹配后,age是{:?}",age);
解构 Option
Option 枚举用来解决 Rust 中变量是否有值的问题,定义:
enum Option<T> {
Some(T),
None,
}
由于 Option、Some 和 None 都包含在 prelude 中,因此可以直接通过名称来使用而无需使用 Option::Some 这种形式使用,但别忘了 Some 和 None 都是 Option 下的枚举成员。
匹配 Option
使用 Option<T>,是为了从 Some 中取出其内部的 T 的值并处理没有值的情况,例如:
fn plus_one(x: Option<i32>) -> Option(i32) {
match x {
Some(i) => Some(i + 1), //将返回的值 i+1 用 Some 进行包裹
None => None,
}
}
传入参数 Some(5) 返回值 Some(6),传入参数 None 返回值 None。
模式适用场景
模式
模式是 Rust 中的特殊语法,用来匹配类型中的结构和数据,往往和 match 表达式联用,以实现强大的模式匹配能力。模式一般由以下内容组合而成:
- 字面值
- 解构的数组、枚举、结构体或者元组
- 变量
- 通配符
- 占位符
所有可能用到模式的地方
match 分支
match 的每个分支就是一个模式,match 匹配需要穷尽,可以使用 _ 匹配剩余所有情况:
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
_ => EXPRESSION,
}
if let 分支
if let 往往用于匹配一个模式而忽略剩下的所有模式的场景。
if let PATTERN = SOME_VALUE {
// ...
}
while let 条件循环
while let 条件循环与 if let 结构类似,它允许只要模式匹配就一直进行 while 循环,示例代码:
// Vec是动态数组
let mut stack = Vec::new();
// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);
// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() {
println!("{}", top);
}
只要 pop() 返回 Some 就会继续循环,直到返回 None。也可以使用 loop + if let 或者 match 实现这个功能,但是更麻烦。
for 循环
for (index, value) in v.iter().enumerate() {
println!("{} is at index {}", value, index);
}
这里使用 enumerate 方法产生一个迭代器,该迭代器每次迭代会返回一个 (索引,值) 形式的元组,然后用 (index,value) 来匹配,也是一种模式匹配。
let 语句
let PATTERN = EXPRESSION;
let x = 5;
let (x, y, z) = (1, 2, 3); //将一个元组与模式进行匹配
let 语句也是一种模式绑定,代表将匹配的值绑定到变量上。因此在 Rust 中变量名也是一种模式。模式在匹配时,模式和值的类型必须相同,否则会报错。
函数参数
函数参数也是模式,比如可以在参数中匹配元组:
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
if 和 if let
类似 let 和 for、match 都必须要求完全覆盖匹配,才能通过编译。但对于 if let 允许匹配一种模式而忽略其他模式。
全模式列表
《Rust语言圣经》中给出了常用的所有模式匹配语法,方法在使用相关内容时查阅(模式匹配在开发中会经常有用到)。
其中涉及到的模式匹配有:
-
匹配命名变量
-
单分支多模式
-
通过序列
..=匹配值的范围 -
解构并分解值
- 解构结构体
- 解构枚举
- 解构嵌套的结构体和枚举
- 结构结构体和元组
-
忽略模式中的值
- 使用
_忽略整个值 - 使用嵌套的
_忽略部分值 - 使用下划线开头忽略未使用的变量
- 用
..忽略剩余值
- 使用
-
匹配守卫提供的额外条件
- 匹配守卫 ( match guard )是一个位于
match分支模式之后的额外if条件,它能为分支模式提供更进一步的匹配条件。
- 匹配守卫 ( match guard )是一个位于
-
@绑定
@(读作 at)运算符允许为一个字段绑定另外一个变量。@前绑定后解构@新特性

浙公网安备 33010602011771号