掌握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   。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911035.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【SpringBoot】18 上传文件到数据库(Thymeleaf + MySQL)

Git仓库 https://gitee.com/Lin_DH/system 介绍 使用 Thymeleaf 写的页面&#xff0c;将&#xff08;txt、jpg、png&#xff09;格式文件上传到 MySQL 数据库中。 依赖 pom.xml <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --><depende…

数据库_SQLite3

下载 1、更新软件源&#xff1a; sudo apt-get update 2、下载SQLite3&#xff1a; sudo apt-get install sqlite3 3、验证&#xff1a; sqlite3启动数据库&#xff0c;出现以下界面代表运行正常。输入 .exit 可以退出数据库 4、安装sqlite3的库 sudo apt-get install l…

全星魅-物联网定位终端-北斗定位便携终端-北斗有源终端

在当今快速发展的物流运输行业中&#xff0c;精准定位与实时监控已成为确保货物安全与高效运输的关键因素。为了满足这一需求&#xff0c;QMCZ10作为一款集4G&#xff08;LTE Cat1&#xff09;通讯技术与智能定位功能于一体的终端产品&#xff0c;应运而生。它不仅具备普通定位…

微服务系列六:分布式事务与seata

目录 实验环境说明 前言 一、分布式事务问题与策略 1.1 分布式事务介绍 1.2 分布式事务解决策略分析 二、分布式事务解决方案 Seata 2.1 认识Seata 2.2 Seata的工作原理 2.3 部署Seata微服务 2.3.1 准备数据库表 2.3.2 准备配置文件 2.3.3 docker部署 2.4 微服务集…

【数字图像处理+MATLAB】对图片进行伽马校正(Gamma Correction):使用 imadjust 函数进行伽马变换

引言 伽马校正&#xff08;Gamma Correction&#xff09;是一种用于图像处理的技术&#xff0c;主要用于调整图像的亮度或对比度。其基本原理是对图像的每一个像素应用一个非线性变换&#xff0c;以更好地适应人眼的视觉感知。在数字图像处理中&#xff0c;伽马校正通常用于调…

数据挖掘实战-基于SARIMA时间序列模型预测Netflix股票未来趋势

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Permissions 0755 for ‘/etc/ssh/ssh_host_rsa_key‘ are too open.问题解决

1、问题背景 代码上库公司git后,将项目上出的程序烧录到设备中,wifi能够正常链接&#xff0c;但是通过wifi链接 ssh登录设备失败。把调试串口引出,查看linux启动log,发现如下打印信息&#xff1a; WARNING: UNPROTECTED PRIVATE KEY FILE! Permissions 075…

第三十五篇:HTTP报文格式,HTTP系列二

HTTP 是超⽂本传输协议&#xff0c;也就是HyperText Transfer Protocol。 前面我们讲到第三章中网络协议的定义&#xff0c;网络协议的定义&#xff1a;网络协议是通信计算机双方必须共同遵从的一组约定。就像两个人要进行交流&#xff0c;如果不制定一套约定&#xff0c;一方…

嵌入式学习(8)-EventOS 事件驱动管理

一、概述 EventOS&#xff0c;是一个面向单片机、事件驱动的嵌入式开发平台。它主要有两大技术特色&#xff1a;一是事件驱动&#xff0c;二是超轻量。 EventOS的主要特性列举如下&#xff1a; &#xff08;1&#xff09;事件总线为核心组件&#xff0c;灵活易用&#xff0c…

json字符串与python字典的区别与联系

json字符串与python中自带的字典类型外表长的很像&#xff0c;很容易区分不清楚&#xff0c;它们之间有着本质的区别&#xff0c;可以通过内置的json模块来互相转换。 文章目录 1、Python字典2、JSON数据格式3、JSON与python字典的区别4、JSON与python字典相互转换4.1 json字符…

Ubuntu 20.04 安装 QGC v4.3 开发环境

Ubuntu 20.04 安装 QGC开发环境 1. 准备安装 Qt 5.15.2安装依赖获取源码 2. 编译参考 前言 QGC ( QGroundControl) 是一个开源地面站&#xff0c;基于QT开发的&#xff0c;有跨平台的功能。可以在Windows&#xff0c;Android&#xff0c;MacOS或Linux上运行。它可以将PX4固件加…

FFmpeg 4.3 音视频-多路H265监控录放C++开发十二:在屏幕上显示多路视频播放,可以有不同的分辨率,格式和帧率。

上图是在安防领域的要求&#xff0c;一般都是一个屏幕上有显示多个摄像头捕捉到的画面&#xff0c;这一节&#xff0c;我们是从文件中读取多个文件&#xff0c;显示在屏幕上。 一 改动UI文件 这里我们要添加两个label&#xff0c;为了区分我们设置一下背景色&#xff08;这个是…

【数据集】【YOLO】【目标检测】道路垃圾识别数据集 8805 张,垃圾堆放识别数据集,YOLO垃圾识别算法实战训练教程!

数据集介绍 【数据集】道路垃圾识别、垃圾堆放识别数据集 8805 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。 数据集中包含2种分类&#xff1a;{0: garbage, 1: garbage_bag}&#xff0c;分别是普通垃圾和垃圾袋。 数据集来自国内外图片网站和视频截图&#x…

《TCP/IP网络编程》学习笔记 | Chapter 4:基于TCP的服务器端/客户端(2)

《TCP/IP网络编程》学习笔记 | Chapter 4&#xff1a;基于TCP的服务器端/客户端&#xff08;2&#xff09; 《TCP/IP网络编程》学习笔记 | Chapter 4&#xff1a;基于TCP的服务器端/客户端&#xff08;2&#xff09;回声客户端的完美实现回声客户端的问题回声客户端问题的解决方…

类加载过程详解

类的生命周期 类从被加载到虚拟机内存中开始到卸载出内存为止&#xff0c;它的整个生命周期可以简单概括为 7 个阶段&#xff1a;加载&#xff08;Loading&#xff09;、验证&#xff08;Verification&#xff09;、准备&#xff08;Preparation&#xff09;、解析&#xff08…

03-构建数据中台的三要素:方法论、组织和技术

03-构建数据中台的三要素&#xff1a;方法论、组织和技术 知道要转型&#xff0c;要建设数据中台&#xff0c;却不知咋做&#xff0c;咋办&#xff1f; 现在有很多讲“如何建设数据中台”文章&#xff0c;观点各不相同&#xff1a; 数据中台是数据建设方法论&#xff0c;按照数…

华为Mate70前瞻,鸿蒙NEXT正式版蓄势待发,国产系统迎来关键一战

Mate 70系列要来了 上个月&#xff0c;vivo、小米、OPPO、荣耀等众多智能手机制造商纷纷发布了他们的年度旗舰产品&#xff0c;手机行业内竞争异常激烈。 同时&#xff0c;华为首席执行官余承东在其个人微博上透露&#xff0c;Mate 70系列将标志着华为Mate系列手机达到前所未有…

源代码防泄密管理分享

随着信息技术的快速发展&#xff0c;软件已成为现代企业不可或缺的核心资产之一。然而&#xff0c;源代码作为软件的心脏&#xff0c;其安全性直接关系到企业的核心竞争力。为了有效防止源代码泄露&#xff0c;构建一套全面且高效的源代码安全管理体系显得尤为重要。以下是六个…

从神经元到神经网络:深度学习的进化之旅

神经元、神经网络 神经元 Neuron )&#xff0c;又名感知机( Perceptron )&#xff0c;在模型结构上与 逻辑回归 一致&#xff0c;这里以一个二维输入量的例子对其进行进一步 的解释&#xff1a; 假设模型的输 入向 量是一 维特征向 (x1,x2). 则单神 经元的模型结构 如下…

[C语言]strstr函数的使用和模拟实现

1.strstr函数的使用 char * strstr ( const char *str1, const char * str2); 返回一个指向str1中str2第一次出现的指针&#xff0c;如果str2中没有str1则返回 NULL。。 实例&#xff1a; #include <stdio.h> #include <string.h> int main() {char str[] "…