Rust中的枚举和模式匹配

专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C++、和python历史悠远,但是它的优点可以说是非常的多,既继承了C++运行速度,还拥有了Java的内存管理,就我个人来说,还有一个优点就是集成化的编译工具cargo,语句风格和C++极其相似,所以说我本人还是比较喜欢这个语言,特此建立这个专栏,作为学习的记录分享。

日常分享:每天努力一点,不为别的,只是为了日后,能够多一些选择,选择舒心的日子,选择自己喜欢的人!


前面我们提到过,Rust中没有了switch case这种模式控制语句,但是喃,除此之外,却又多了另一种匹配规则,那就是模式匹配。所以这节我们就来聊聊模式匹配这种匹配机制。

1、枚举

枚举其实在c++中就有过定义,二者相差不大,关键字是enum,枚举和结构体一样,也是用来自定义的数据类型。

说到枚举,可能有些同学还不是特别清楚,枚举的意义在那里,其实枚举他只是一个存放字段的一种容器吧,在后面的代码中,如果你需要多种字段,但是你又不是特别明确具体需要哪些,就把所有可能的字段放在其中,需要什么就使用什么。

enum Error{typeError,lengthError,
}

 例如上面的代码,定义了一个Error的枚举类型,这个时候,Error就是一个数据类型。

1.1、枚举值

let oneError = Error::typeError;let twoEroor = Error::lengthError;

 就如上面的代码,我们定义了两个实例对象,而他们的数值则是Error中的两个字段。

这里注意的是,枚举的成员位于其标识符的命名空间中,并使用两个冒号分开。

但是有人看到这里就会有疑惑,这里的枚举类型中的字段都是没有具体值的,那么我们如何将值与枚举成员关联?

上一节讲解了结构体的概念,这里我们就可以使用结构体来进行绑定:


enum Error{typeError,lengthError,
}struct getError{oneError: Error,twoEroorderError: Error,address:String,
}
let amples = getError{oneError: Error::typeError,twoEroorderError: Error::lengthError,address:String::from("Hello world"),
};

 上面所时代码就是将枚举作为结构体的一部分,除了上面的方法,我们似乎还可以使用其他方法,例如将数据放进每一个枚举成员。

enum Error{typeError(String),lengthError(String),
}
let oneError = Error::typeError(String::from("one error"));
let twoEroor = Error::lengthError(String::from("two Eroor error"));

1.2、Option枚举

前面写的代码中,对于枚举数据类型,虽然将值通过结构体进行了绑定,但是却没有具体的值,只有通过将值放进枚举成员,才能获得值。那么没有的值又是什么?或者说又有什么作用?

Rust语言和其他语言的一点不同就在于它没有空值,也就是说不能赋予空值,必须去实现。Rust 并没有空值,不过它确实拥有一个可以编码存在或不存在概念的枚举。

enum Option<T>{
None,
Some(T),
}

 Option<T> 枚举是如此有用以至于它甚至被包含在了 prelude 之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 Option:: 前缀来直接使用 SomeNone。这里要注意,Option枚举是含在标准库的,不需要我们定义,直接使用,上面知识给出参考。

<T> 语法是一个我们还未讲到的 Rust 功能。它是一个泛型类型参数,后面遇到了我们再详细介绍。

  let some_number = Some(5);let some_char = Some('e');let number:Option<i32>=None;

上面的三条语句,变量some_number的类型是i32,some_char的类型是char,而number的类型是i32,只是是一个空值。 对于Option<T>,这里的T可以是任何数据类型,除了赋空值外,一般来说不需要注明变量的数据类型,除非是特殊需要,Rust可以推断其变量的数据类型。如果是赋空值,就必须注明变量的数据类型,否则会报错。

不过这里需要注意的是,Option<T>标注的数据类型与相同的数据类型变量不能进行运算.

  let one_num:Option<i32>=some(20);let s:i32=30;println!("{}",one_num+s);

error[E0425]: cannot find function `some` in this scope
  --> src/main.rs:45:27
   |
45 |   let one_num:Option<i32>=some(20);
   |                           ^^^^ help: a tuple variant with a similar name exists (notice the capitalization): `Some`

error[E0369]: cannot add `i32` to `Option<i32>`
  --> src/main.rs:47:24
   |
47 |   println!("{}",one_num+s);
   |                 -------^- i32
   |                 |
   |                 Option<i32>

Some errors have detailed explanations: E0369, E0425.
For more information about an error, try `rustc --explain E0369`.
error: could not compile `number` due to 2 previous errors

当运行上述代码的时候就会出现这种报错,这是为什么喃?这是由于当我们使用Option<T> 数据类型的时候就表明该数据可能为空,而我们使用i32(或其他数据类型)的时候,就已经表明改变两不可能为空值,所以才会出现报错,根本原因还是在与其数据类型被系统判定为两种数据类型.

换句话说,在对 Option<T> 进行运算之前必须将其转换为 T。通常这能帮助我们捕获到空值最常见的问题之一:假设某值不为空但实际上为空的情况。

消除了错误地假设一个非空值的风险,会让你对代码更加有信心。为了拥有一个可能为空的值,你必须要显式的将其放入对应类型的 Option<T> 中。接着,当使用这个值时,必须明确的处理值为空的情况。只要一个值不是 Option<T> 类型,你就 可以 安全的认定它的值不为空。这是 Rust 的一个经过深思熟虑的设计决策,来限制空值的泛滥以增加 Rust 代码的安全性。

那么当有一个 Option<T> 的值时,如何从 Some 成员中取出 T 的值来使用它呢?Option<T> 枚举拥有大量用于各种情况的方法:你可以查看它的文档。熟悉 Option<T> 的方法将对你的 Rust 之旅非常有用。

总的来说,为了使用 Option<T> 值,需要编写处理每个成员的代码。你想要一些代码只当拥有 Some(T) 值时运行,允许这些代码使用其中的 T。也希望一些代码只在值为 None 时运行,这些代码并没有一个可用的 T 值。match 表达式就是这么一个处理枚举的控制流结构:它会根据枚举的成员运行不同的代码,这些代码可以使用匹配到的值中的数据。

2、match控制流结构

我们前面说了在Rust语言中没有switch这种控制流语句,但是它却推出了match这种强大的控制流运算符。在python语言中,match是正则表达式中的匹配函数,所以这里也可以理解为匹配函数。

先来看个例子:

enum error_message{E0425,E0369,E2345,
}
fn get_error_message(message:error_message)->u32{match message{error_message::E0425 =>{println!("cannot find function `some` in this scope");return 0;}error_message::E0369=>{println!("cannot add `i32` to `Option<i32>");return 1;}error_message::E2345=>{println!("could not compile `number` due to 2 previous errors");return 2;}}
}
fn main(){let mut error=get_error_message(error_message::E2345);println!("{}",error);get_error_message(error_message::E0425);
}

就如上面的代码,先是定义了一个枚举数据类型,然后定义了一个函数,在函数中使用了match控制流。根据不同的参数值,返回不同的值,并打印出结果。

1.1、绑定值模式

匹配分支的另一个有用的功能是可以绑定匹配的模式的部分值。这也就是如何从枚举成员中提取值的。

/*
enum error_message{E0425,E0369,E2345,
}
fn get_error_message(message:error_message)->u32{match message{error_message::E0425 =>{println!("cannot find function `some` in this scope");return 0;}error_message::E0369=>{println!("cannot add `i32` to `Option<i32>");return 1;}error_message::E2345=>{println!("could not compile `number` due to 2 previous errors");return 2;}}
}
fn main(){let mut error=get_error_message(error_message::E2345);println!("{}",error);get_error_message(error_message::E0425);
}
*/
#[derive(Debug)]
enum UsState{Alabama,Alaska,
}
enum Coin{Penny,Nickel,Dime,Quarter(UsState),
}
fn value_cents(coin:Coin) -> usize {match coin{Coin::Penny =>{return 1;}Coin::Nickel =>{return 5;}Coin::Dime =>{return 10;}Coin::Quarter(state)=>{println!("State quarter from {:#?}",state);return 25;}}
}
fn main()
{let b=UsState::Alaska;let c=Coin::Quarter(b);value_cents(c);}

 如果调用 value_in_cents(Coin::Quarter(UsState::Alaska))coin 将是 Coin::Quarter(UsState::Alaska)。当将值与每个分支相比较时,没有分支会匹配,直到遇到 Coin::Quarter(state)。这时,state 绑定的将会是值 UsState::Alaska。接着就可以在 println! 表达式中使用这个绑定了,像这样就可以获取 Coin 枚举的 Quarter 成员中内部的州的值。

1.2、匹配Option<T>

我们在之前的部分中使用 Option<T> 时,是为了从 Some 中取出其内部的 T 值;我们还可以像处理 Coin 枚举那样使用 match 处理 Option<T>!只不过这回比较的不再是硬币,而是 Option<T> 的成员,但 match 表达式的工作方式保持不变。

例如:

fn plus_amount(amount:Option<i32>)->Option<i32> 
{match amount {None=>{println!("该值为空值");return None;}Some(i)=>{println!("该值不是空值,值为{}",i);return Some(i);}}
}
fn main(){let five=Some(5);let num1=plus_amount(five);plus_amount(None);
}

1.3、通配模式和_占位符

其实除了枚举,match控制流也可以用于其他形式,比如:

let num=30;match num{10=>{println!("10");}11 => println!("11"),other => println!("other"),}

 上面的代码中,我们在最后使用了other这个变量,这个变量覆盖了所有其他的可能值,除了我们列出来的可能性,other会包含所有的其他可能性,所以other一定要放在最后,否无法达到目的。

不过在这里因该有人发现了,other会绑定match匹配的值,这个我们将上面的程序更改一下,就能的出这个结论:

fn main(){let five=Some(5);let num1=plus_amount(five);plus_amount(None);let num=30;match num{10=>{println!("10");}11 => println!("11"),other => println!("other的值为:{}",other),}
}

 other的值为:30

上面就是输出结果,这说明other绑定到了match匹配的值上,这样做的好处就是可以获得匹配值,将其进行使用,但是如果我们不需要使用那个值,这样做就有点浪费,所以Rust也推出了_占位符,占位符只是表示可以匹配任意值而不能绑定到该值。

fn main(){let five=Some(5);let num1=plus_amount(five);plus_amount(None);let num=30;match num{10=>{println!("10");}11 => println!("11"),_ => println!("other"),}
}

 3、if let间接控制流

Rust中的if let控制流说的简单点,就相当于c++中的if else语句,对于一些简单的判别,使用if let控制流语句将会简单很多。例如:

fn main() 
{let config=Some(3u8);match config{Some(max)=>println!("{}",max),_=>(),}
}

上面面代码的意思是,匹配config的值,如果值是Some,就将值绑定到max变量上,然后输出,否则就忽略。

除了上面这样的方式,我们还可以使用其他的方式:

fn main() 
{let config=Some(3u8);if let Some(max)=config{println!("{}",max);}
}

这样看来是不是就简单的多了,所以说从某种角度来看,if let语句确实简单了很多。

#[derive(Debug)]
enum UsState{Alabama,Alaska,
}
enum Coin{Penny,Nickel,Dime,Quarter(UsState),
}
fn main() 
{let mut count=0;let b=UsState::Alaska;let coin=Coin::Quarter(b);//以下两种方式都可以/*match coin{Coin::Quarter(state)=>{println!("State quarter from {:#?}",state);}_=>count+=1,}*/if let Coin::Quarter(state) = coin{println!("State quarter from {:#?}",state);}else{count+=1;}
}

4、总结

现在我们涉及到了如何使用枚举来创建有一系列可列举值的自定义类型。我们也展示了标准库的 Option<T> 类型是如何帮助你利用类型系统来避免出错的。当枚举值包含数据时,你可以根据需要处理多少情况来选择使用 matchif let 来获取并使用这些值。

你的 Rust 程序现在能够使用结构体和枚举在自己的作用域内表现其内容了。在你的 API 中使用自定义类型保证了类型安全:编译器会确保你的函数只会得到它期望的类型的值。

下一节我们学习模块系统。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Antv/s2 明细表 透视表实现和性能优化(一)

前言 以我实际项目环境为准&#xff0c;vuets为技术框架&#xff0c;代码如果有什么不懂欢迎留言评论我会回复的 透视表 定义文件 class PivotTableControl extends BaseControl {type pivotTable;label controls.chart.pivotTable;icon tc-color-pivot-table;widget () &…

目标识别项目实战:基于Yolov7-LPRNet的动态车牌目标识别算法模型(三)

前言 目标识别如今以及迭代了这么多年&#xff0c;普遍受大家认可和欢迎的目标识别框架就是YOLO了。按照官方描述&#xff0c;YOLOv8 是一个 SOTA 模型&#xff0c;它建立在以前 YOLO 版本的成功基础上&#xff0c;并引入了新的功能和改进&#xff0c;以进一步提升性能和灵活性…

平凡工作也能创造卓越:学习公文写作的逻辑与技巧

平凡工作也能创造卓越&#xff1a;学习公文写作的逻辑与技巧 前言如何把平凡的工作写出光环1.个人不能超越集体2.工作成果的概括要准确3.描写平凡工作的难点痛点 书籍介绍关键点关键词 书籍亮点内容简介购买链接参与方式往期赠书回顾 前言 如何把平凡的工作写出光环 &#x1…

docker部署Vaultwarden密码共享管理系统

Vaultwarden是一个开源的密码管理器&#xff0c;它是Bitwarden密码管理器的自托管版本。它提供了类似于Bitwarden的功能&#xff0c;允许用户安全地存储和管理密码、敏感数据和身份信息。 Vaultwarden的主要特点包括&#xff1a; 1. 安全的数据存储&#xff1a;Vaultwarden使…

运行在移动设备上的ML机器学习任务——基于MediaPipe的手势识别

前期的文章我们介绍了MediaPipe的人手关键点检测。其检测的21个点的坐标位置如下: 当检测到其关键点后,我们就可以利用此关键点来进行人手手势识别。MediaPipe 手势识别器任务可实时识别手势,并提供识别的手势结果。我们可以使用此任务来识别用户的特定手势,并调用与这些手…

Andriod 简单控件

目录 一、文本显示1.1 设置文本内容1.2 设置文本大小1.3 设置文本颜色 二、视图基础2.1 设置视图宽高2.2 设置视图间距2.3 设置视图对齐方式 三、常用布局3.1 线性布局LinearLayout3.2 相对布局RelativeLayout3.3 网格布局GridLayout3.4 滚动视图ScrollView 四、按钮触控4.1 按…

SwiftUI Spacer() onTapGesture 无法触发

问题&#xff1a;点击这个黑色区域不会 print&#xff0c;黑色区域看上去刚好是 Spacer() 占据的区域 解决办法&#xff1a;不使用 onTapGesture&#xff0c;用 Button 包裹一下 Code: import SwiftUIstruct TestTap: View {var body: some View {NavigationStack {List {Sect…

正则验证用户名和跨域postmessage

一、正则验证用户名 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title> </head> <body> <form action"/login" method"post"><input type…

完美解决 flex 实现一行三个,显示多行,左对齐

效果图 代码 <body><section class"content"><div class"item">元素</div><div class"item">元素</div><div class"item">元素</div><div class"item">元素</di…

Node.js操作MySQL8.0数据库无法连接

Node.js操作MySQL8.0数据库无法连接 原创&#xff1a;丶无殇  2023-10-07 报错内容 使用node.js连接数据库MySQL 8时候&#xff0c;报错ER_NOT_SUPPORTED_AUTH_MODE&#xff0c;并且提示Client does not support authentication protocol requested by server; consider upg…

【python】可视化-绘制带有边权重的无向图

文章目录 需求示例数据代码实现 需求 输入数据表(矩阵)&#xff0c;绘制无向图。 示例数据 **示例数据1&#xff1a;**3个特征之间的关系数据 (data1.txt) featuresfeature1feature2feature3feature110.60.8feature20.610.3feature30.80.31 **示例数据2&#xff1a;**4个特…

2023年中国汽车座舱行业发展现状及趋势分析:高级人机交互(HMI)系统将逐步提升[图]

2022年有22.3%的汽车用户认为座舱内车载娱乐功能成为影响使用体验的关键因素。当前智能电动汽车的用户画像与娱乐、游戏等应用的用户画像相似&#xff0c;均以年轻人作为目标用户。年轻化的用户将娱乐功能的使用习惯延伸至汽车座舱内&#xff0c;对于座舱功能的需求不再局限于导…

C++ 33.学习C++的意义-狄泰软件学院

一些历史 UNIX操作系统诞生之初是直接用汇编语言编写的随着UNIX系统的发展&#xff0c;汇编语言的开发效率成为瓶颈&#xff0c;所以需要一个新的语言替代汇编语言1971年通过对B语言改良&#xff0c;使其能直接产生机器代码&#xff0c;C语言诞生UNIX使用C语言重写&#xff0c…

mysql双主互从通过KeepAlived虚拟IP实现高可用

mysql双主互从通过KeepAlived虚拟IP实现高可用 在mysql 双主互从的基础上&#xff0c; 架构图&#xff1a; Keepalived有两个主要的功能&#xff1a; 提供虚拟IP&#xff0c;实现双机热备通过LVS&#xff0c;实现负载均衡 安装 # 安装 yum -y install keepalived # 卸载 …

智能AI系统源码ChatGPT系统源码+详细搭建部署教程+AI绘画系统+已支持OpenAI GPT全模型+国内AI全模型

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统&#xff0c;支持OpenAI GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Chat…

层次架构、面向服务架构(四十四)

层次架构设计 表现层、中间层、数据访问层、数据架构规划、物联网层次架构、层次式架构案例分析。 层次结构缺点就是效率问题&#xff0c;上一层调用下一层。 1、着重写中间层 组件设计&#xff1a;面向接口编程&#xff0c;分为接口和实现类。 实体设计&#xff1a;实体表…

无需公网IP,教学系统如何实现远程一站式管理维护?

全国多所高校应用红亚科技研发的一套教学实验系统平台&#xff0c;此实验系统服务器分别部署在学校内部&#xff0c;与校内的各种教学资源整合在一起&#xff0c;向校内师生提供服务。 红亚总部设立在北京&#xff0c;虽说在全国22个省会均设有办事处&#xff0c;在面对全国多…

【ESP32 + Edge Impulse平台】运行AI算法模拟多传感器数据融合实现异常检测

本篇博文主要以ESP32+MQ Sensor 气体传感器为例,通过连接 Edge Impulse 平台,实现数据的实时采集和训练,进而实现在嵌入式设备上部署 ML 机器学习。本教程介绍如何使用 Edge Impulse 和机器学习来实现ESP32 异常检测系统,系统使用一个机器学习模型,检测气体何时出现异常。…

JOSEF约瑟 闭锁继电器 LB-7 YDB-100 100V 50HZ 控制断路器的合闸或跳闸

闭锁继电器LB-7导轨安装名称:闭锁继电器型号:LB-7闭锁继电器额定电压100V功率消耗≤10VA触点容量220V1.5A40W返回系数≥0.8 LB-1A、LB-1D、DB-1、HBYB-102/D YDB-100、HLO、DB-100、LB-7型闭锁继电器 一、用途 LB-7型闭锁继电器(以下简称继电器)用于发电厂及变电所内高压母线…

Vim教程

目录 vim 介绍 常用的四种模式 首先先学会如何正确进入和退出vim&#xff1a; normal模式 insert模式&#xff1a; command模式&#xff1a; v-block模式&#xff1a; vim异常退出 vim配置 vim 介绍 Vim是一款高度可定制的文本编辑器&#xff0c;它的前身是Vi&#xf…