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::
前缀来直接使用 Some
和 None
。这里要注意,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>
类型是如何帮助你利用类型系统来避免出错的。当枚举值包含数据时,你可以根据需要处理多少情况来选择使用 match
或 if let
来获取并使用这些值。
你的 Rust 程序现在能够使用结构体和枚举在自己的作用域内表现其内容了。在你的 API 中使用自定义类型保证了类型安全:编译器会确保你的函数只会得到它期望的类型的值。
下一节我们学习模块系统。
相关文章:

Rust中的枚举和模式匹配
专栏简介:本专栏作为Rust语言的入门级的文章,目的是为了分享关于Rust语言的编程技巧和知识。对于Rust语言,虽然历史没有C、和python历史悠远,但是它的优点可以说是非常的多,既继承了C运行速度,还拥有了Java…...

好物周刊#19:开源指北
https://github.com/cunyu1943/JavaPark https://yuque.com/cunyu1943 村雨遥的好物周刊,记录每周看到的有价值的信息,主要针对计算机领域,每周五发布。 一、项目 1. Vditor 一款浏览器端的 Markdown 编辑器,支持所见即所得、…...

分布式数据库(林子雨慕课课程)
文章目录 4. 分布式数据库HBase4.1 HBase简介4.2 HBase数据模型4.3 HBase的实现原理4.4 HBase运行机制4.5 HBase的应用方案4.6 HBase安装和编程实战 4. 分布式数据库HBase 4.1 HBase简介 HBase是BigTable的开源实现 对于网页搜索主要分为两个阶段 1.建立整个网页索引…...
使用UiPath和AA构建的解决方案 3. CRM 自动化
您是否曾经从一个应用程序中查找数据并更新另一个系统? 在许多情况下,人们在系统之间复制和移动数据。有时,可能会发生“转椅活动”,从而导致人为失误。RPA可以帮助我们自动化这些活动,使其更快,同时还可以消除任何人为错误。 在这个项目中,我们将在客户服务中自动化一…...

【C++设计模式之状态模式:行为型】分析及示例
简介 状态模式(State Pattern)是一种行为型设计模式,它允许对象在内部状态改变时改变其行为,看起来就像是改变了其类。状态模式将对象的状态封装成不同的类,并使得对象在不同状态下有不同的行为。 描述 状态模式通过…...

微信小程序使用路由传参和传对象的方法
近期在做微信小程序开发,在页面跳转时,需要携带参数到下一个页面,尤其是将对象传入页面。为了方便重温,特此记录。 路由传字符串参数 原始页面 传递字符串参数比较简单。路由跳转有两种方式,一种是通过navigator组件…...
中国创可贴市场研究与未来预测报告(2023版)
内容简介: 创可贴由胶布(带)、吸水垫、防粘层等组成,胶布以弹性布、棉布、无纺布或PE、PVC、PU打孔膜、TPU等材料为常见基材,涂以氧化锌和橡胶为主要原料的胶浆或医用压敏胶黏剂或丙烯酸酯胶粘剂制成。 目前中国主要…...

水库安全监测方案(实时数据采集、高速数据传输)
一、引言 水库的安全监测对于防止水灾和保障人民生命财产安全至关重要。为了提高水库安全监测的效率和准确性,本文将介绍一种使用星创易联DTU200和SG800 5g工业路由器部署的水库安全监测方案。 二、方案概述 本方案主要通过使用星创易联DTU200和SG800 5g工业路…...
vue项目 ueditor使用示例
简介 UEditor是由百度Web前端研发部开发的所见即所得富文本web编辑器,具有轻量,功能丰富,易扩展等特点。UEditor支持常见的文本编辑功能,如字体、颜色、大小、加粗、斜体、下划线、删除线等,同时还支持超链接、图片上…...

深度学习笔记之优化算法(四)Nesterov动量方法的简单认识
机器学习笔记之优化算法——Nesterov动量方法的简单认识 引言回顾:梯度下降法与动量法Nesterov动量法Nesterov动量法的算法过程描述总结 引言 上一节对动量法进行了简单认识,本节将介绍 Nesterov \text{Nesterov} Nesterov动量方法。 回顾:…...

比 N 小的最大质数
系列文章目录 进阶的卡莎C++_睡觉觉觉得的博客-CSDN博客数1的个数_睡觉觉觉得的博客-CSDN博客双精度浮点数的输入输出_睡觉觉觉得的博客-CSDN博客足球联赛积分_睡觉觉觉得的博客-CSDN博客大减价(一级)_睡觉觉觉得的博客-CSDN博客小写字母的判断_睡觉觉觉得的博客-CSDN博客纸币(…...
JavaScript 生成随机颜色
代码 function color(color) {return (color "0123456789abcdef"[Math.floor(Math.random() * 6)]) && (color.length 6 ? color : arguments.callee(color)); }使用 // 用法1:全部随机生成 "#" color(""); // #201050…...
Savepoints
语法 SAVEPOINT 名称 RELEASE SAVEPOINT 名称 ROLLBACK TRANSACTION TO SAVEPOINT 名称 Savepoints 与BEGIN和COMMIT类似的创建事务的方法,名称不要求唯一且可以嵌套使用。 可以用在BEGIN…COMMIT定义的事务内部或外部。当在外部时,最外层的savepoin…...

【MySQL】基本查询(二)
文章目录 一. 结果排序二. 筛选分页结果三. Update四. Delete五. 截断表六. 插入查询结果结束语 操作如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chi…...

Qt:多语言支持,构建全面应用程序“
Qt:强大API、简化框架、多语言支持,构建全面应用程序" 强大的API:Qt提供了丰富的API,包括250多个C类,基于模板的集合、序列化、文件操作、IO设备、目录管理、日期/时间等功能。还包括正则表达式处理和支持2D/3D…...

性能监控-链路级监控工具
常见的链路监控工具,我们都称之为 APM 开源工具 几个开源的好用的工具,它们分别是 Pinpoint、SkyWalking、Zipkin、CAT 网络上也有人对这几个工具做过测试 比对,得到的结论是每个产品对性能的影响都在 10% 以下,其中 SkyWalking …...
clickonce 程序发布到ftp在使用cnd 加速https 支持下载,会不会报错
ClickOnce 是一种用于发布和部署.NET应用程序的技术,通常用于本地部署或通过网络分发应用程序。将 ClickOnce 程序发布到 FTP 服务器并使用 CDN(内容分发网络)进行加速是可能的,但要确保配置正确以避免出现错误。 在使用 CDN 加速…...

Nginx详细学习记录
1. Nginx概述 Nginx是一个轻量级的高性能HTTP反向代理服务器,同时它也是一个通用类型的代理服务器,支持绝大部分协议,如TCP、UDP、SMTP、HTTPS等。 1.1 Nginx基础架构 Nginx默认采用多进程工作方式,Nginx启动后,会运行…...

golang gin——中间件编程以及jwt认证和跨域配置中间件案例
中间件编程jwt认证 在不改变原有方法的基础上,添加自己的业务逻辑。相当于grpc中的拦截器一样,在不改变grpc请求的同时,插入自己的业务。 简单例子 func Sum(a, b int) int {return a b }func LoggerMiddleware(in func(a, b int) int) f…...

如何快速制作令人惊叹的长图海报
在当今的数字时代,制作一张吸引人的长图海报已成为许多人的需求。无论是为了宣传活动,还是展示产品,一张设计精美的长图海报都能引起人们的注意。下面,我们将介绍一种简单的方法,使用在线海报制作工具来创建长图海报。…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...