掌握Rust模式匹配:从基础语法到实际应用

本篇文章将探讨 Rust 编程语言中至关重要的特性之一------模式匹配。Rust 语言的模式匹配功能强大,不仅能处理简单的值匹配,还能解构和操作复杂的数据结构。通过深入学习模式匹配,程序员可以更加高效地编写出清晰、简洁且易于维护的代码。

Rust 语言中的模式匹配是一种特殊的语法结构,用于匹配变量、解构数组、结构体、枚举和元组等。本文主要介绍了 Rust 中各种模式的使用场景,包括 match、if let、while let、for 循环以及函数参数。除了基础语法外,还讲解了可辩驳性和无可辩驳性、字面值匹配、命名变量、多重模式、范围匹配、结构解构等模式的使用细节。

模式

  • 模式是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 if
    • else 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 if_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);
    
    let (q, w) = (4, 5, 6); // 报错 类型不匹配 3 2
    

    }

函数参数

  • 函数参数也可以是模式

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

    fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
    }

    fn main() {
    let point = (3, 5);
    print_coordinates(&point);
    }

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

模式的两种形式

  • 模式有两种形式:可辨驳的、无可辩驳的

  • 能匹配任何可能传递的值的模式:无可辩驳的

    • 例如:let x = 5;
  • 对某些可能得值,无法进行匹配的模式:可辩驳的

    • 例如:if let Some(x) = a_value
  • 函数参数、let 语句、for 循环只接受无可辩驳的模式

  • if let 和 while let 接受可辨驳和无可辩驳的模式

    fn main() {
    let a: Option<i32> = Some(5);
    let Some(x) = a: // 报错 None
    if let Some(x) = a {}
    if let x = 5 {} // 警告
    }

三、模式语法

匹配字面值

  • 模式可直接匹配字面值

    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);
    

    }

多重模式

  • 在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!("something else"),
  }
  
  let x = 'c';
  match x {
    'a' ..='j' => println!("early ASCII letter"),
    'k' ..='z' => println!("late ASCII letter"),
    _ => println!("something else"),
  }
}

解构以分解值

  • 可以使用模式来解构 struct、enum、tuple,从而引用这些类型值的不同部分

    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::ChangeClolr(Color::Rgb(r, g, b)) => {
      println!("Change the color to red {}, green {}, and blur {}", 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(_)) => {
      println!("Can't overwrite an existing customized value");
    }
    _ => {
      setting_value = new_setting_value;
    }
  }
  
  println!("setting is {:?}", setting_value);
  
  
  let numbers = (2, 4, 6, 8, 16, 32);
  
  match numbers {
    (first, _, third, _, fifth) => {
      println!("Some numbers: {}, {}, {}", first, third, fifth)
    }
  }
}

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

fn main() {
  let _x = 5;
  let y = 10;  // 创建未使用 警告
  
  let s = Some(String::from("Hello"));
  
  if let Some(_s) = s { // if let Some(_) = 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 条件,想要匹配该条件也必须满足
  • 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,
      } => {
        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)
      }
    }
    

    }

总结

模式匹配在 Rust 中是一个灵活而强大的特性,为程序的控制流提供了极大的灵活性。通过使用 match、if let、while let 等语法,程序员可以在匹配结构中定义和解构不同的数据类型,简化代码逻辑。深入理解模式匹配的用法,将帮助开发者编写更具表达力的代码,为 Rust 编程带来新的思路与技巧,更多相关信息, https://t.me/gtokentool

相关推荐
java亮小白19977 分钟前
Spring循环依赖如何解决的?
java·后端·spring
chnming198715 分钟前
STL关联式容器之map
开发语言·c++
进击的六角龙17 分钟前
深入浅出:使用Python调用API实现智能天气预报
开发语言·python
檀越剑指大厂17 分钟前
【Python系列】浅析 Python 中的字典更新与应用场景
开发语言·python
VertexGeek18 分钟前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
2301_8112743124 分钟前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
湫ccc24 分钟前
Python简介以及解释器安装(保姆级教学)
开发语言·python
程序伍六七28 分钟前
day16
开发语言·c++
wkj00133 分钟前
php操作redis
开发语言·redis·php
极客代码38 分钟前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow