【Fitz】Rust 模式匹配

说明:本文主要是Rust语言圣经相关章节的学习笔记,大部分与其内容相同,欢迎阅读原文。

match 和 if let

Rust 中,模式匹配最常用的就是 matchif 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! 宏,可以将一个表达式跟模式进行匹配,然后返回匹配的结果 truefalse

例如有动态数组,里面存有枚举:

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,
}

由于 OptionSomeNone 都包含在 prelude 中,因此可以直接通过名称来使用而无需使用 Option::Some 这种形式使用,但别忘了 SomeNone 都是 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

类似 letformatch 都必须要求完全覆盖匹配,才能通过编译。但对于 if let 允许匹配一种模式而忽略其他模式。


全模式列表

《Rust语言圣经》中给出了常用的所有模式匹配语法,方法在使用相关内容时查阅(模式匹配在开发中会经常有用到)。

其中涉及到的模式匹配有:

  • 匹配命名变量

  • 单分支多模式

  • 通过序列 ..= 匹配值的范围

  • 解构并分解值

    • 解构结构体
    • 解构枚举
    • 解构嵌套的结构体和枚举
    • 结构结构体和元组
  • 忽略模式中的值

    • 使用 _ 忽略整个值
    • 使用嵌套的 _ 忽略部分值
    • 使用下划线开头忽略未使用的变量
    • .. 忽略剩余值
  • 匹配守卫提供的额外条件

    • 匹配守卫match guard )是一个位于 match 分支模式之后的额外 if 条件,它能为分支模式提供更进一步的匹配条件。
  • @绑定

    • @(读作 at)运算符允许为一个字段绑定另外一个变量。
    • @ 前绑定后解构
    • @ 新特性
posted @ 2022-03-17 21:49  AlphaFitz  阅读(290)  评论(0)    收藏  举报