当前位置: 首页 > news >正文

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 中使用自定义类型保证了类型安全:编译器会确保你的函数只会得到它期望的类型的值。

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

相关文章:

Rust中的枚举和模式匹配

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

好物周刊#19:开源指北

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

分布式数据库(林子雨慕课课程)

文章目录 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.建立整个网页索引&#xf…...

使用UiPath和AA构建的解决方案 3. CRM 自动化

您是否曾经从一个应用程序中查找数据并更新另一个系统? 在许多情况下,人们在系统之间复制和移动数据。有时,可能会发生“转椅活动”,从而导致人为失误。RPA可以帮助我们自动化这些活动,使其更快,同时还可以消除任何人为错误。 在这个项目中,我们将在客户服务中自动化一…...

【C++设计模式之状态模式:行为型】分析及示例

简介 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;看起来就像是改变了其类。状态模式将对象的状态封装成不同的类&#xff0c;并使得对象在不同状态下有不同的行为。 描述 状态模式通过…...

微信小程序使用路由传参和传对象的方法

近期在做微信小程序开发&#xff0c;在页面跳转时&#xff0c;需要携带参数到下一个页面&#xff0c;尤其是将对象传入页面。为了方便重温&#xff0c;特此记录。 路由传字符串参数 原始页面 传递字符串参数比较简单。路由跳转有两种方式&#xff0c;一种是通过navigator组件…...

中国创可贴市场研究与未来预测报告(2023版)

内容简介&#xff1a; 创可贴由胶布&#xff08;带&#xff09;、吸水垫、防粘层等组成&#xff0c;胶布以弹性布、棉布、无纺布或PE、PVC、PU打孔膜、TPU等材料为常见基材&#xff0c;涂以氧化锌和橡胶为主要原料的胶浆或医用压敏胶黏剂或丙烯酸酯胶粘剂制成。 目前中国主要…...

水库安全监测方案(实时数据采集、高速数据传输)

​ 一、引言 水库的安全监测对于防止水灾和保障人民生命财产安全至关重要。为了提高水库安全监测的效率和准确性&#xff0c;本文将介绍一种使用星创易联DTU200和SG800 5g工业路由器部署的水库安全监测方案。 二、方案概述 本方案主要通过使用星创易联DTU200和SG800 5g工业路…...

vue项目 ueditor使用示例

简介 UEditor是由百度Web前端研发部开发的所见即所得富文本web编辑器&#xff0c;具有轻量&#xff0c;功能丰富&#xff0c;易扩展等特点。UEditor支持常见的文本编辑功能&#xff0c;如字体、颜色、大小、加粗、斜体、下划线、删除线等&#xff0c;同时还支持超链接、图片上…...

深度学习笔记之优化算法(四)Nesterov动量方法的简单认识

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

比 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&#xff1a;全部随机生成 "#" color(""); // #201050…...

Savepoints

语法 SAVEPOINT 名称 RELEASE SAVEPOINT 名称 ROLLBACK TRANSACTION TO SAVEPOINT 名称 Savepoints 与BEGIN和COMMIT类似的创建事务的方法&#xff0c;名称不要求唯一且可以嵌套使用。 可以用在BEGIN…COMMIT定义的事务内部或外部。当在外部时&#xff0c;最外层的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&#xff1a;强大API、简化框架、多语言支持&#xff0c;构建全面应用程序" 强大的API&#xff1a;Qt提供了丰富的API&#xff0c;包括250多个C类&#xff0c;基于模板的集合、序列化、文件操作、IO设备、目录管理、日期/时间等功能。还包括正则表达式处理和支持2D/3D…...

性能监控-链路级监控工具

常见的链路监控工具&#xff0c;我们都称之为 APM 开源工具 几个开源的好用的工具&#xff0c;它们分别是 Pinpoint、SkyWalking、Zipkin、CAT 网络上也有人对这几个工具做过测试 比对&#xff0c;得到的结论是每个产品对性能的影响都在 10% 以下&#xff0c;其中 SkyWalking …...

clickonce 程序发布到ftp在使用cnd 加速https 支持下载,会不会报错

ClickOnce 是一种用于发布和部署.NET应用程序的技术&#xff0c;通常用于本地部署或通过网络分发应用程序。将 ClickOnce 程序发布到 FTP 服务器并使用 CDN&#xff08;内容分发网络&#xff09;进行加速是可能的&#xff0c;但要确保配置正确以避免出现错误。 在使用 CDN 加速…...

Nginx详细学习记录

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

golang gin——中间件编程以及jwt认证和跨域配置中间件案例

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

如何快速制作令人惊叹的长图海报

在当今的数字时代&#xff0c;制作一张吸引人的长图海报已成为许多人的需求。无论是为了宣传活动&#xff0c;还是展示产品&#xff0c;一张设计精美的长图海报都能引起人们的注意。下面&#xff0c;我们将介绍一种简单的方法&#xff0c;使用在线海报制作工具来创建长图海报。…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...