Rust 基础知识23 - 模式匹配

模式匹配

  • 模式匹配通常由一下组件组成:

字面量。

解构的数组、枚举、结构体或者元组。

变量。

通配符

占位符。

知识汇总

模式的不可失败性和可失败性

  • 模式可以被分为不可失败(irrefutable) 和可失败(refutable)两种类型。
  • 例如 let x = 5; 中的 x 便是不可失败模式,因为它能够匹配表达式右侧的所有可能的返回值。
  • 例如 if let Some(x) = a_value 中的 Some(x) 便是可失败模式,如果 a_value 变量的值时 None 而不是 Some,那么表达式左侧的Some(x) 模式就会发生不匹配。
  • 了解这些概念十分重要,比如 let Some(x) = a_value 就会造成编译困扰,因为这个表达式是可失败的那么就要放到 if 中去。

if let 表达式

  • 举例:
fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age : Result = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        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");
        }
    }
}

while let 条件循环模式匹配

  • 例子:while let 会反复执行同一个模式匹配直到出现失败的情况。

fn main() {
    let mut stack = Vec::new();
    stack.push(1);
    stack.push(2);
    stack.push(3);
    while let Some(s) = stack.pop() {
        println!("Stack value : {}", s);
    }
}

for 循环进行元祖填充

  • 例子:注意不要忘记调用 enumerate 生成一个元祖迭代器

fn main() {
    let v = vec!["a", "b", "c"];
    for (index, value) in v.iter().enumerate() {
        println!("index : {}, value : {}",index, value );
    }
}

let 关键字本身就是一种模式匹配的方式

  • 例子:
// 如果模式元素中的数量与元祖的数量不符,那么就会导致匹配失败。
let (x, y, z) = (1, 2, 3);
// 如果确实有的不需要,可以通过 _ 或者 .. 进行省略
let (x, y, _) = (1, 2, 3);

函数参数也是模式匹配

  • 可以看到模式匹配无处不在,函数的参数也是模式匹配。
  • 例子:
// 实际上定义了一个元祖参数
fn print_coordinates(&(x, y): &(i32, i32)) {
    println!(" Current location : ({}, {})", x, y);
}

fn main() {
    let point = (55, 33);
    print_coordinates(&point);
}

匹配字面量

  • 例子:
fn main() {
    let x = 1 ;
    match x {
        1 => {println!("One"); },
        2 => {println!("Two"); },
        // 注意下面必须加上如果不加会提示没有穷尽所有解
        _ => {println!("Other"); },
    }
}

匹配命名变量

  • 例子:
fn main() {
    let x = Some(5);
    let y = 10 ;
    match x {
        Some(50) => println!("Get 50"),
        Some(y) => println!("Matched, y = {:?}", y ),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end : x = {:?}, y = {:?}", x, y);
}

多重匹配

  • 例子:
fn main() {
    let x = 1 ;
    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything."),
    }
}

使用 ... 来匹配区间

  • 例子:
fn main() {
    let x = 7;
    match x {
        1 ... 9 => println!("one through nine"),
        3 => println!("three"),
        _ => println!("anything."),
    }
}

使用 ..= 来匹配字符区间

  • 例子:
fn main() {
    let x = 'c';
    match x {
        'a' ..= 'd' => println!("a | b | c | d"),
        'f' ..= 'i' => println!("f | g | h | i"),
        _ => println!("anything."),
    }
}

使用解构来分解值(分解结构)

  • 例子:
fn main() {

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

    let point = Point {
        x: 32,
        y: 66
    };

    // 解构
    let Point {x : a, y : b } = point;
    println!("x:{} , y:{}", a,  b);
}

稍稍复杂一点的match 匹配

  • 看了这个例子突然感觉模式匹配很有用,会的:
fn main() {
    struct Point {
        x: i32,
        y: i32,
    }
    let point = Point {
        x: 0,
        y: 7
    };
    // 进行更复杂一点的模式匹配
    match point {
        Point{x, y:0} => println!("On the x axis at {}", x),
        Point{x:0, y} => println!("On the y axis at {}", y),
        // 注意如下最不能缺少的就是这个,没有这个兜底,那么匹配就不能穷尽。
        Point{x,y} => println!("On neither axis: ({}, {})", x,y),
    }
}

解构嵌套的结构体和枚举:

  • 例子:
enum Color {
    Rgb (i32,i32,i32),
    Hvs (i32,i32,i32),
}

enum Message {
    Quit,
    Write(String),
    ChangeColor(Color),
}

fn main() {
    let msg = Message::ChangeColor(Color::Rgb(6,6,6));
    match msg {
        Message::ChangeColor(Color::Rgb(r,g,b)) => {
            println!("r={},g={},b={}", r,g,b);
        },
        Message::ChangeColor(Color::Hvs(h,v,s)) => {
            println!("h={},v={},s={}", h,v,s);
        },
        _ => {}
    }
}

使用 _ (下划线) 忽略变量值时需要特别注意的一个问题

  • 例子,错误的

fn main() {
    let s = Some(String::from("Hello."));
    if let(_s) = s {
        println!("Hello , into there.")
    }
    // 这段代码编译不过去,虽然 _s 但是所有权仍然会转移
    println!("s value is : {:?} ", s);
}

  • 例子,正确的,_ 单纯的下滑线就不会造成所有权转移,稍稍修改一下。
fn main() {
    let s = Some(String::from("Hello."));
    // 修改一下这里,去掉 _ 后面的任何值,所有权就不会转移。
    if let(_) = s {
        println!("Hello , into there.")
    }
    println!("s value is : {:?} ", s);
}

使用 .. (双点) 忽略匹配值

  • 例子:

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

fn main() {
    let p = Point {x:3,y:4,z:5};
    // 如果只是想匹配x 可以使用双点语法。
    let Point{x, ..} = p;
    assert_eq!(3, x);
}

为了匹配更复杂的模式可以使用match guard

  • 就是在模式匹配中新增一个 if 条件,举例:
fn main() {
    let num = Some(4);
    match num {
        Some(x) if x < 5 => { println!("Num {} less than five. ", x) },
        Some(x) => { println!("Num is {} . ", x) },
        None => {},
    }
}


  • 注意一个情况:
match x {
    4 | 5 | 6 if y => { ... } 
}

实际上等于

match x {
    (4 | 5 | 6) if y => { ... }
    // 而不是 
    4 | 5 | (6 if y) => { ... }
}

模式匹配中的绑定

  • 有些范围匹配即便成功了,我们也无法捕获到底是什么值匹配成功的。
  • 这时候就需要在匹配中绑定变量需要通过 @ 运算符。
fn main() {
    enum Message {
        Hello {id: i32}
    }

    let msg = Message::Hello {id: 5};

    match msg {
        // 注意这里,如果不用 id_variable@ 进行变量捕获,下面的println 就无法直到具体的值是什么。
        Message::Hello {id: id_variable@ 3..=7} => {
            println!("Found an id in range : {} ", id_variable);
        },
        Message::Hello {id:10...12} => {
            println!("Found id between 10 to 12");
        },
        Message::Hello {id} => {
            println!("Found some other id : {}", id);
        },
    }
}

结束

  • 感谢阅读,See you at work.

你可能感兴趣的:(Rust 基础知识23 - 模式匹配)