• Rust 从入门到精通09-模式解构


    “Pattern Destructure” 是 Rust 中一个重要且实用的设计。通常翻译为 “模式解构”。

    Destructure 单词翻译为把原来的结构肢解为单独的、原始的部分。

    下面我们举例说明什么是模式解构:

    fn main() {
        let tul = (1,"a",false);
        let (head,center,tail) = tul;
        println!("{}",head);//1
        println!("{}",center);//a
        println!("{}",tail);//false
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第2行代码是构造;第3行代码是拆解,产生了三个新的变量。

    Rust 的模式解构不仅出现在 let 语句中,还可以出现在 match、if let、while let、函数调用、闭包调用等情景中。

    1、match

    1.1 用于匹配多种取值

    rustenum Direction{
        East,
        West,
        South,
        North,
    }
    fn print(x:Direction){
        match x {
            Direction::East => {
                println!("East");
            }
            Direction::West => {
                println!("West");
            }
            Direction::South => {
                println!("South");
            }
            Direction::North => {
                println!("North");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    当一个类型有多种取值时,特别适合适用 match 表达式。可以等价于 if-else

    1.2 必须匹配到每个分支exhaustive

    exhuastive:彻底的,详尽的

    对应到 match 表达式上,Rust 要求 match 需要对所有的情况做彻底的,无遗漏的匹配。比如对于上面的例子,我们删除一个匹配分支(East)

    注意:编译器会保证 match 所有分支合起来一定覆盖了所有的情况,但是不保证各个分支重复的问题。

    enum Direction{
        East,
        West,
        South,
        North,
    }
    fn print(x:Direction){
        match x {
            Direction::West => {
                println!("West");
            }
            Direction::South => {
                println!("South");
            }
            Direction::North => {
                println!("North");
            }
        }
    }
    fn main() {
        print(Direction::West);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    编译就会报错:

    1.3 一个分支匹配多种类型 |

    我们可以通过 | 在一个分支匹配多种情况;

    enum Direction{
        East,
        West,
        South,
        North,
    }
    fn print(x:Direction){
        match x {
            Direction::East => {
                println!("East");
            }
            Direction::West | Direction::South  | Direction::North=> {
                println!("Other");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.3 通配模式和 _ 占位符

    有时候,我们并不知道到底有多少种匹配情况,这时候可能需要一个类似通配符,来匹配所有没有考虑的情况,从而避免编译失败。

    fn print(x:Direction){
        match x {
            Direction::West => {
                println!("West");
            }
            Direction::South => {
                println!("South");
            }
            other => {
                println!("orther");
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上面代码前面两个分支我们匹配了 West 和 South,剩下两个分支我们直接定义了一个名为 other 的变量(这个变量可以任意命名),它将匹配所有未被特殊列出来的值。

    需要注意:我们必须将通配分支放在最后,因为模式是按顺序匹配的。如果我们在通配分支后添加其他分支,Rust 将会警告我们,因为此后的分支永远不会被匹配到。

    除此之外,Rust 还推出了"_" 占位符来匹配所有情况。

    enum Direction{
        East,
        West,
        South,
        North,
    }
    fn print(x:Direction){
        match x {
            Direction::East => {
                println!("East");
            }
            _ => {
                println!("other");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    1.4 忽略匹配占位符 _

    下划线除了用作 match 表达式中的分支额外匹配之外,还可以用来表示一个占位符,表示虽然匹配到了,但是忽略掉。

    struct P(i32,i32,i32);
    fn add(p:P) -> i32{
        let P(x,_,y) = p;
        x+y
    }
    fn main() {
        let p = P(1,2,3);
        println!("{}",add(p));//4
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们只想求元祖结构体P的第一个和末尾数据之和,所以中间的数据可以通过_ 忽略掉。

    1.5 match guards 匹配看守

    match 和 if 配合一起使用。

    enum OptionalInt{
        Value(i32),
        Missing,
    }
    fn match_if(op:OptionalInt){
        match op {
            OptionalInt::Value(i) if i > 10 => {println!("big int")}
            OptionalInt::Missing => println!("not int")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是上面这段代码会编译错误,提示没有覆盖所有的情况。(如果移除 if 条件,是正常编译的)

    那么如何修改这个问题呢?我们增加 if 匹配条件看看:

    fn match_if(op:OptionalInt){
        match op {
            OptionalInt::Value(i) if i > 10 => {println!("big int")},
            OptionalInt::Value(i) if i<= 10 => {println!("get int")},
            OptionalInt::Missing => println!("not int"),
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    发现还是编译失败

    我们已经通过 if 覆盖了所有的情况,但是还是不能编译通过,这其实也是 rust 编译器目前无法做到的,它毕竟不是一个完整的数学解析器。

    这种情况下,我们加个下划线匹配就可以了。

    fn match_if(op:OptionalInt){
        match op {
            OptionalInt::Value(i) if i > 10 => {println!("big int")},
            OptionalInt::Value(i) if i<= 10 => {println!("get int")},
            _ => println!("没有特殊意义"),
            OptionalInt::Missing => println!("not int"),
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2、if-let

    比如有一个类型为 Option 的变量 val,我们要取出里面的值。

    ①、使用match表达式

    fn getOptionVal(str:Option<String>) -> String{
        match str {
            Some(x) => x,
            None => panic!("called `Option::unwrap()` on a `None` value")
        }
    
    }
    fn main() {
        let val = Some("ab".into());
        println!("{}",getOptionVal(val));//ab
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ②、if 表达式

    fn getOptionVal(str:Option<String>) -> String{
        if str.is_some(){
            return str.unwrap();
        }
        panic!("called `Option::unwrap()` on a `None` value")
    }
    fn main() {
        let val = Some("ab".into());
    
        println!("{}",getOptionVal(val));//ab
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ③、if-let

    fn getOptionVal(str:Option<String>) -> String{
        if let Some(x) = str{
            return x;
        }
        panic!("called `Option::unwrap()` on a `None` value")
    }
    fn main() {
        let val = Some("ab".into());
        println!("{}",getOptionVal(val));//ab
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第二种方法(if 表达式)取值分两步,第一步调用 is_some()判断,第二步调用unwrap()取值;

    而if-let 看上去就简洁很多,但是和 if 表达式相比并没有效率上的差别,只是rust提供的一个语法糖。

  • 相关阅读:
    python轻量级性能工具-Locust
    Linux端口的开启
    粮食淘洗机结构设计(说明书+任务书+开题+文献综述+翻译及原文+cad图纸)
    Python--列表及其应用场景
    电磁逆设计中伴随变量法的详细指南:Python在Jupyter环境下的完整演示
    133道Java面试题及答案(面试必看)
    BathchData数据分批处理
    Spring Boot 整合Hibernate Validator
    网络技术九:生成树协议
    【从头构筑C#知识体系】0.3 值类型和引用类型
  • 原文地址:https://blog.csdn.net/ysvae/article/details/126376256