rust match 匹配用法

51.png

模式匹配,增强版的 switch

  1. 可以匹配各种类型(不只是整数或字符串)
  2. 能解构复杂的数据结构(比如元组、枚举、结构体)
  3. 是表达式,返回值
  4. 要求穷尽所有可能(不会漏掉情况)

语法规则

match 要匹配的值 {
    模式1 => {...},
    模式2 => {...},
    模式3 => {...},
    ...
}

✅ 整数匹配

#[test]
fn match_script() {
    let number = 5;
    match number {
        1 => println!("1111"),
        2 => println!("2222"),
        3..=7 => println!("闭区间"),   // 3 4 5 6 7
        9..13 => println!("半开区间"), // 9 10 11 12
        _ => println!("default"),      // 通配符,匹配其余所有
    }
}

_ 表示通配符,匹配其余所有,3..=7 3..7 区间写法

✅ 字符切片匹配

#[test]
fn match_script() {
    let input = "yes"; // &str 可拷贝类型

    match input {
        "yes" => println!("Affirmative!"),
        "no" => println!("Negative!"),
        _ => println!("Unknown response"),
    }

    println!("input: {}", input); // 可正常输出
}

和整数匹配一样,匹配完所有权也还在

✅ 枚举匹配

#[derive(Debug)]
enum Rgb {
    Red,
    Green,
    Blue,
}

#[test]
fn match_script() {
    let color = Rgb::Red;
    match color {
        Rgb::Red => println!("Red"),
        Rgb::Green => println!("Green"),
        Rgb::Blue => println!("Blue"),
    }
}

这里会打印Red,match要求匹配的值每个可能的case都要实现,硬性规定

这样写会报错

let color = Rgb::Red;
match color {
    Rgb::Red => println!("Red"),
    Rgb::Green => println!("Green"),
}

✅ Option 枚举匹配

业务场景: 通常用于获取内容,比如数据库查询只会有两个结果,一个是值内容,一个是无内容

#[test]
fn match_script() {
    let maybe_value: Option<i32> = Some(41);

    match maybe_value {
        Some(x) => println!("Got a value: {}", x),
        None => println!("No value"),
    }

    let maybe_none: Option<i32> = None;
    match maybe_none {
        Some(x) => println!("Got a value: {}", x),
        None => println!("No value"),
    }
}

output

Got a value: 41
No value

枚举匹配,会将变体的值带入,例如 Some(x) => println!("Got a value: {}", x), 匹配上就是 Some(41), => 后面的相当于函数实现,x 相当于实参

✅ Result 枚举匹配

业务场景: 通常用于结果资源,比如数据库连接只会有两个结果,一个是连接成功,一个是连接错误

use std::fs::File;

#[test]
fn match_script() {
    let maybe_value: Result<&str, &str> = Ok("successful");
    match maybe_value {
        Ok(x) => println!("Got a value: {}", x),
        Err(e) => println!("Error : {}", e),
    }

    // 打开文件
    match File::open("Cargo.lock") {
        Ok(_file) => println!("Opened file successfully"),
        Err(error) => println!("Error opening file: {}", error),
    }
}

output

Got a value: successful
Opened file successfully
  1. Result 和 Option 枚举匹配用法一样
  2. _file表示这个变量暂时没用到

✅ 结构形式匹配

✅ 解构元组

#[test]
fn match_script() {
    let maybe_tuple = (0, "msg", "data");
    match maybe_tuple {
        (0, x, "data") => {
            println!("tuple.code: {:?}", x);
        }
        (0, "msg", "data_list") => {
            println!("tuple is {:?}", maybe_tuple);
        }
        (0, x, y) => println!("tuple.msg: {:?} data: {:?} ", x, y),
        _ => println!("tuple is not match"),
    }
}

output

tuple.code: "msg"
  1. match要求匹配的各种可能性都要实现,如果不想写那么多,最后加一个_通配默认实现即可
  2. 匹配中也支持具体值匹配,例如 (0, "msg", "data_list")(0, x, y) 都是可以匹配上的
  3. 匹配参数支持混合,(0, x, "data") (z, x, y)
  4. 最终输出的是(0, x, "data")分支,而不是(0, x, y),优先匹配上面的代码

还是原来的代码,现在换个 case 顺序

#[test]
fn match_script() {
    let maybe_tuple = (0, "msg", "data");
    match maybe_tuple {
        (0, x, y) => println!("tuple.msg: {:?} data: {:?} ", x, y),
        (0, x, "data") => {
            println!("tuple.code: {:?}", x);
        }
        (0, "msg", "data_list") => {
            println!("tuple is {:?}", maybe_tuple);
        }
        _ => println!("tuple is not match"),
    }
}

这种编译就通不过了,第一个 case 已经涵盖了第二个 case,代码冗余了

✅ 解构结构体

struct Point {
    x: i32,
    y: i32,
}

#[test]
fn match_script() {
    let p = Point { x: 10, y: 10 };
    match p {
        Point { x: 0, y } => println!("match Point.y: {}", y),
        Point { x, y } => println!("Point: ({}, {})", x, y),
    }
}

output
Point: (10, 10)

这里我的Point { x, y }已经是所有可能性实现了,再加_ => println!("match _"),就会报错了

✅ 使用守卫

在 case 中添加 if 语句

match variable {
    val if 条件 => {...}
}
#[test]
fn match_script() {
    let num = 7;
    match num {
        n if n < 5 => println!("{} is less than 5", n),
        n if n % 2 == 0 => println!("{} is even and >=5", n),
        _ => println!("{} is odd and >=5", num),
    }
}

output

7 is odd and >=5

✅ 引用匹配

#[test]
fn match_script() {
    let s = String::from("hello");

    match &s[..] {
        "hello" => println!("Greeting!"),
        _ => println!("Something else"),
    }

    // s 仍然可用
    println!("s is still alive: {}", s);
}

output

Greeting!
s is still alive: hello

&s[..] 有两层意思

[..] 为范围索引取值,语法[a..b],

  • a:起始索引(包含,默认 0);
  • b:结束索引(不包含,默认值为类型的长度);
  • ..:省略 start 和 end 时,代表 “从开头到结尾” 的全范围。

&s 在这里作用为 String -> &str 类型

match 匹配 String,先转化为字符切片,与 case 数据类型一致,这是常规写法

在这里demo中, match &s[..] 等价于 match &s

再来看一个示例

#[test]
fn match_script() {
    let x = Some(5);
    match &x {
        Some(val) => println!("x is {}", val),
        None => println!("None"),
    }

    println!("x is alive: {}", x.unwrap());

    let y = Some(6);
    match y {
        Some(val) => println!("y is {}", val),
        None => println!("None"),
    }

    println!("y is alive: {}", y.unwrap());
}

output

x is 5
x is alive: 5
y is 6
y is alive: 6

x y 都正常输出了,通过 x.as_ref() 借用值使用自然没问题,后续所有权依旧可以使用

但是 5 是可拷贝类型,Some(5) 是枚举啊???

这里有个规则:枚举的所有变体的所有关联数据都实现了 Copy 时,枚举才能派生 / 自动实现 Copy

Option 里只有 Some 变体关联了 T,Some(5) 也就意味着该 Option 实现了可 Copy

pub enum Option<T> {
    None,
    Some(T),
}

换成下面这种写法就会报错,String 不是可拷贝类型

#[test]
fn match_script() {
    let x = Some(String::from("hello"));
    match x {
        Some(val) => println!("x is {}", val),
        None => println!("None"),
    }

    println!("x is alive: {}", x.unwrap()); // x 的所有权已经给了 match,如果此处要运行,就需要改成 match &x
}

✅ 多条件匹配

#[test]
fn match_script() {
    let day = 6;

    match day {
        1 | 2 | 3 | 4 | 5 => println!("Weekday"),
        6 | 7 => println!("Weekend"),
        _ => println!("Invalid day"),
    }
}

一笔带过

✅ 返回值

#[test]
fn match_script() {
    let status_code = 404;

    let message = match status_code {
        200 => "OK",
        404 => "Not Found",
        500 => "Internal Server Error",
        _ => "Unknown",
    };

    println!("Status: {}", message);
}

output

Status: Not Found

match 不仅仅起 switch 的作用,还有返回值可以用

如果有变量接收当返回值用时,每个 case 就要有 return 了(上面例子单一字符川也是return)

let message = match status_code {
    200 => "OK",
    404 => println!("Not Found"),
    500 => "Internal Server Error",
    _ => "Unknown",
};

这种编译不过的

✅ Vec/数组匹配

#[test]
fn match_script() {
    let arr = [1, 2, 8];

    match arr {
        [1, 2, 3] => println!("Exact match"),
        [x, y, ..] => println!("First: {}, Second: {}", x, y),
    }
}

output

First: 1, Second: 2
  1. 匹配向量和数组,长度必须是固定的,比方说你 case 里面写 [1, 2, 3, 8] 就会报错
  2. [x, y, ..] 这种写法会通配匹配, x, y为匹配的向量前两个值,后面的..为通配作用,这个..在一个 case模式 中只允许出现 1 次,位置随意,[.., y, z] [x, .., z]

✅ 写法变种

if let

if 写法式的 match

这种方式通常用于业务中只处理一种模式时,起 if 作用逻辑

#[test]
fn match_script() {
    let optional_target = Some("rustlings");
    if let Some(word) = optional_target {
        println!("match {}", word)
    }

    // ......
}

output

match rustlings

等价于下面写法,相当于省去了所有模式的实现

let optional_targets = Some("rustlings");
match optional_targets {
    Some(word) => println!("match {}", word),
    None => {}
}
// ......

while let

while 写法式的 match

常用于重复从某个可变结构中取出值,直到不满足某种模式为止。

非常用于数组向量、元组这类复合数据类型循环遍历

#[test]
fn match_script() {
    let mut optional_integers: Vec<Option<i32>> = vec![Some(3), Some(2), Some(1)];
    let mut cursor = 1;

    while let Some(Some(n)) = optional_integers.pop() {
        assert_eq!(n, cursor);
        cursor += 1;
    }
    println!("✅ while let works!");
}

output

✅ while let works!
  1. 这里本质是将 while 与 match 的语法结合起来
  2. pop方法是出栈,注意哈,pop的返回值也是一个 Option<T>,这里在取值时要解析两层 Option,Some(Some(n))
  3. pop出完栈就没了,组合变量声明表达式 (let = xxx) 这种写法,形成了一个有限的 while 循环

等价于下面代码

#[test]
fn match_script() {
    let mut optional_integers: Vec<Option<i32>> = vec![Some(3), Some(2), Some(1)];
    let mut cursor = 1;
    loop {
        match optional_integers.pop() {
            Some(Some(n)) => {
                assert_eq!(n, cursor);
                cursor += 1;
            }
            Some(None) => {}
            None => break, // 向量所有值取完了
        }
    }
    println!("✅ while let works!");
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容