18 模式与模式匹配
模式是Rust中的特殊语法,用来匹配类型中的结构,无论类型复杂与否。模式由以下一些内容组合而成:
字面值\解构的数组、枚举、元组或者结构体\变量\通配符\占位符,这些部分描述了我们需要处理的数据形状。(匹配模式其实可以理解为匹配项,简单理解就是把两个项目拉起来比较,看等不等于,等于的话我们就执行一些代码,不等于的话执行另一些代码。这个点就跟条件语句外加等于号一样,只不过Rust中把它专门做了个匹配模式)
18.1 所有可能会遇到模式的位置
Rust中有很多地方都会用到匹配模式
match 分支
我们在之前就见过match 表达式,我们这里再来复习一下,它的组陈元素match关键字、用于匹配的值VALUE和一个或者多个分支。当分支模式匹配到时,执行分支后的表达式
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
match表达式必须穷尽,我们可以使用特定的模式_匹配所有情况,不过它从不绑定任何变量
if let 条件表达式
if let 我们之前也了解过,它只关心一种情况,当然它可以再带有一个可选的else,在if let 中的模式不匹配时运行
我们再来看一个例子:if let \ else if let\ else if 表达式的组合运用,这样比match表达式更灵活,它们分支条件也并不要求相互关联
fn main() {
let favorite_color:Option<&str> = None;
let is_tuseday = false;
let age:Result<u8,_> = "34".parse();
if let Some(color) =favorite_color {
println!("Using your favorite color, {}, as the background",color);
}else if is_tuseday {
println!("Tuesday is green day!")
}else if let Ok(age)=age {
if age >30 {
println!("Using purple as the background color");
}else {
println!("Using orange as the background color");
}
} else {
println!("Using the blue as the background color");
}
}
如果用户指定了颜色,那么使用其作为背景颜色,如果今天是星期二,那么背景色为绿色,如果用户指定了年龄并且能够被解析为数字,根据数字大小指定是紫色还是橙色
我们可以使用类似于这样的表达式实现复杂的需求
注意:if let也可以像match分支那样引入覆盖变量,if let Ok = age引入了一个新的覆盖变量age,它包含Ok成员中的值,这意味着if age >30 需要位于这个代码块中,不能将两个条件组合为if let Ok(age) = age && age > 30,因为我们希望与30进行比较的被覆盖的age直到大括号开始的新作用域才是有效的
if let 表达式的缺点在于其穷尽性没有被编译器所检查,而match则被检查了。这意味着去过去掉最后的else块而遗漏一些处理情况编译器也不会警告这类可能的逻辑错误
while let 条件循环
一个与if let结构类似的是while let 条件循环,它允许只要模式匹配就一直进行while循环
fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}",top);
}
}
使用while let循环只要stack.pop()返回Some就打印出其值
pop方法会取出vector的最后一个元素并返回Some(value),如果vector是空的,它返回None。运行结果
3
2
1
for 循环
for循环是Rust中最常见的一个结构,不过for也可以获取一个模式。在for循环中,模式是for关键字直接跟随的值,正如for x in y 中的x
fn main() {
let v = vec!['a','b','c'];
for (index,value) in v.iter().enumerate() {
println!("{} is at index {}",value,index)
}
}
这里使用enumerate方法适配一个迭代器来产生一个值和其在迭代器中的索引,它们位于一个元组中
第一个enumerate调用会产生元组(0,‘a’),当这个值匹配模式(index,value),index将会是0而value将会是‘a’,并打印出第一行输出
a is at index 0
b is at index 1
c is at index 2
let 语句
let语句其实就是在使用模式
let x = 5;
let (x,y,z) = (1,2,3)
这个解构将元组与模式匹配。Rust会比较值(1,2,3)与模式(x,y,z)并发现此值匹配这个模式,如果错误匹配的话,可以发现如下值:
let (x,y) = (1,2,3);
mismatched types
--> src/main.rs:9:5
|
9 | let (x,y) = (1,2,3);
| ^^^^^ ------- this expression has type `({integer}, {integer}, {integer})`
| |
| expected a tuple with 3 elements, found one with 2 elements
|
= note: expected tuple `({integer}, {integer}, {integer})`
found tuple `(_, _)`
函数参数
函数参数可以是模式,我们来写一个函数
fn foo(x:i32) {
//代码
}
x部分就是一个模式!类似于之前对let所做的,可以在函数参数紫红匹配元组
fn print_coordinates(&(x,y):&(i32,i32)) {
println!("Current location: ({},{})",x,y);
}
fn main() {
let point = (3, 5);
1
print_coordinates(&point);
}
在一个参数中解构元组的函数
这会打印出Current location:(3,5)。值&(3,5)会匹配模式&(x,y),如此x得到了值3,而y得到了值5
闭包类似于函数,也可以在闭包参数列表中使用模式
我们现在已经了解在很多地方都使用模式了,但是模式在每个地方并不以相同的方式工作,接下来我们来讨论这两个值