Rust 学习笔记 - 模式匹配

模式匹配

模式是 Rust 中的一种特殊语法,用于匹配复杂和简单类型的结构。将模式与匹配表达式和其他构造结合使用,可以更好地控制程序的控制流。

模式由以下元素(的一些组合)组成:

  • 字面值
  • 解构的数组、enum、struct 和 tuple
  • 变量
  • 通配符
  • 占位符

想要使用模式,需要将其余某个值进行比较:如果模式匹配,就可以在代码中使用这个值的相应部分。

用到模式匹配的地方

match 的 Arm

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}
复制代码

match 表达式的要求:详尽(包含所有的可能性)。

一个特殊的模式:_(下划线):它会匹配任何东西,不会绑定到变量,通常用于 match 的最后一个 arm,或者用于忽略某些值。

条件 if let 表达式

if let 表达式主要是作为一种简短的方式来等价的代替只有一个匹配项的 match。

if let 可选的可以拥有 else,包括 else ifelse if let,但 if let 不会检查穷尽性

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = 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_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");
        }
    } else {
        println!("Using blue as the background color");
    }
}
复制代码

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);
    }
}
复制代码

for 循环

for 循环是 Rust 中最常见的循环,for 循环中,模式就是紧随 for 关键字后的值。

fn main() {
    let v = vec!['a', 'b', 'c'];

    for (index, value) in v.iter().enumerate() {
        println!("{} is at index {}", value, index);
    }
}
复制代码

let 语句

let 语句也是模式。

let PATTERN = EXPRESSION;
复制代码
fn main() {
    let a = 5;

    let (x, y, z) = (1, 2, 3);
}
复制代码

函数参数

函数参数也可以是模式。

fn foo(x: i32) {
    // code goes here
}

fn print_coodinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
}
fn main() {
    let point = (3, 5);

    print_coodinates(&point);
}
复制代码

可辩驳性:模式是否会无法匹配

模式有两种形式:可辩驳的和无可辩驳的。

能够匹配任何可能传递的值的模式就是无可辩驳的。

例如:let x = 5;

对某些可能的值,无法进行匹配的模式就是可辩驳的。

例如:if let Some(x) = a_value

函数参数、let 语句、for 循环只接受无可辩驳的模式。

if letwhile let 接受可辩驳和无可辩驳的模式。

fn main() {
    let a: Option<i32> = Some(5);

    let Some(x) = a; // 报错

    if let Some(x) = a {}
}
复制代码

Some 是一个可辩驳的,但是 let 要求的是是一个不可辩驳的值。而改成 if let 之后就可以了。

match 表达式前面的都是可辩驳的,但是最后一个表达式是不可辩驳的。

模式匹配的语法

匹配字面值

模式可以直接匹配字面值。

fn main() {
    let x = 1;

    match x {
        1 => println!("one"),
        2 => println!("two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}
复制代码

匹配命名变量

命名的变量是可匹配任何值的无可辩驳模式。

fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("Got 50"),
        Some(y) => println!("Matched, y = {:?}", y),
        _ => println!("Default case, x = {:?}", x),
    }

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

输出

Matched, y = 5
at the end: x = Some(5), y = 10
复制代码

多重模式

在 match 表达式中,使用 | 语法(就是或的意思),可以匹配多种模式。

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}
复制代码

..= 匹配某个范围的值

fn main() {
    let x = 5;

    match x {
        1..=5 => println!("one through five"),
        _ => println!("anything"),
    }

    let x = 'c';
    match x {
        'a'..='j' => println!("early ASCII letter"),
        'k'..='z' => println!("late ASCII letter"),
        _ => println!("something eles"),
    }
}
复制代码

解构以分解值

可以使用模式来解构 structenumtuple,从而引用这些类型值的不同部分。

struct

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

fn main() {
    let p = Point { x: 0, y: 7 };

    let Point { x: a, y: b } = p;

    assert_eq!(0, a);
    assert_eq!(7, b);

    let Point { x, y } = p;
    assert_eq!(0, x);
    assert_eq!(7, y);

    match p {
        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

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

fn main() {
    let msg = Message::ChangeColor(0, 160, 255);

    match msg {
        Message::Quit => {
            println!("The Quit variant has no data to destructure.")
        }
        Message::Move { x, y } => {
            println!("Move in the x direction {} and in the y direction {}", x, y)
        }
        Message::Write(text) => println!("Text Message: {}", text),
        Message::ChangeColor(r, g, b) => {
            println!("Change the color to red {}, green {}, and blue {}", r, g, b)
        }
    }
}
复制代码

struct 和 enum 嵌套

enum Color {
    Rgb(i32, i32, i32),
    Hsv(i32, i32, i32),
}

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(Color),
}
fn main() {
    let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));

    match msg {
        Message::Quit => {
            println!("The Quit variant has no data to destructure.")
        }
        Message::Move { x, y } => {
            println!("Move in the x direction {} and in the y direction {}", x, y)
        }
        Message::Write(text) => println!("Text Message: {}", text),
        Message::ChangeColor(Color:: Rgb(r, g, b)) => {
            println!("Change the color to red {}, green {}, and blue {}", r, g, b)
        }
        Message::ChangeColor(Color:: Hsv(h, s, v)) => {
            println!("Change the color to hue {}, saturation {}, and value {}", h, s, v)
        }
    }
}
复制代码

struct 和 tuple

struct Point {
    x: i32,
    y: i32,
}
fn main() {
    let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
}
复制代码

在模式中忽略值

有几种方式可以在模式中忽略整个值或部分值:

  • _ 可以忽略整个值吗,也可以配合其它模式忽略部分值,或者使用 _ 开头的名称
  • .. 忽略值的剩余部分

使用 _ 忽略整个值

fn foo(_: i32, y: i32) {
    println!("This code only uses the y parameter: {}", y);
}
fn main() {
    foo(3, 4);
}
复制代码

使用嵌套的 _ 来忽略值的一部分

fn main() {
    let mut setting_value = Some(5);
    let new_setting_value = Some(10);

    match (setting_value, new_setting_value) {
        (Some(_), Some(_)) => { // 只要是 Some 就行不管里面的值是什么
            println!("Can't overwrite an existing customized value");
        }
        _ => {
            setting_value = new_setting_value;
        }
    }

    println!("setting is {:?}", setting_value);

    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, _, third, _, fifth) => {
            println!("Some numbers: {}, {}, {}", first, third, fifth)
        }
    }
}
复制代码

通过使用 _ 开头命名来忽略未使用的变量

fn main() {
    let _x = 5; // 定义不使用,没有警告
    let y = 5; // 定义不使用,有警告
}
复制代码
fn main() {
    let s = Some(String::from("Hello!"));

    if let Some(_s) = s { // s 的值会移动到 _s,_s 改成 _ 就不会报错了,_ 不会发生绑定操作
        println!("found a string");
    }
    println!("{:?}", s); // 报错
}
复制代码

使用 .. 来忽略值的剩余部分

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

fn main() {
    let origin = Point { x: 0, y: 0, z: 0 };

    match origin {
        Point {x, ..} => println!("x is {}", x),
    }

    let numbers = (2, 4, 8, 16, 32);

    match numbers {
        (first, .., last) => {
            println!("Some numbers: {}, {}", first, last)
        }
    }

    match numbers {
        (.., second, ..) => { // 发生歧义就会报错
            println!("Some numbers: {}", second)
        }
    }
}
复制代码

使用 match 守卫来提供额外的条件

match 守卫就是 match arm 模式后额外的 if 条件,想要匹配该条件也必须满足这个 if。match 守卫适用于比单独的模式更复杂的场景。

fn main() {
    let num = Some(4);

    match num {
        Some(x) if x < 5 => println!("less than five: {}", x),
        Some(x) => println!("{}", x),
        None => (),
    }
}
复制代码
fn main() {
    let x = Some(5);
    let y = 10;

    match x {
        Some(50) => println!("Got 50"),
        Some(n) if n == y => println!("Matched, n = {:?}", n),
        _ => println!("Default case, x = {:?}", x),
    }

    println!("at the end: x = {:?}, y = {:?}", x, y);
}
复制代码
fn main() {
    let x = 4;
    let y = false;

    match x {
        4 | 5 | 6 if y => println!("yes"),
        _ => println!("no"),
    }
}
复制代码

@绑定

@ 符号让我们可以创建一个变量,该变量可以在测试某个值是否与模式匹配的同时保存该值。

enum Message {
    Hello { id: i32 },
}

fn main() {
    let msg = Message::Hello { id: 5 };
    
    match msg {
        Message::Hello {
            id: id_variable @ 3..=7, // 判断是否在 3~7 范围内,并给变量赋值
        } => {
            println!("Found an id in range: {}", id_variable)
        }
        Message::Hello { id: 10..=12 } => {
            println!("Found an id in another range")
        }
        Message::Hello { id } => {
            println!("Found some other id: {}", id)
        }
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/7041440087430823966