13_Rust中的枚举

news/2024/3/29 17:04:18/文章来源:https://blog.csdn.net/qq_53744721/article/details/127379913

Rust中的枚举

文章目录

  • Rust中的枚举
    • 枚举的基本概念
    • 枚举和其相对于空值的优势
    • if let简单控制流

枚举的基本概念

枚举enumerations),也被称作 enums

枚举允许你通过列举可能的 成员variants) 来定义一个类型。

首先,我们会定义并使用一个枚举来展示它是如何连同数据一起编码信息的。接下来,我们会探索一个特别有用的枚举,叫做 Option它代表一个值要么是某个值要么什么都不是

简单来讲,枚举就是一种类型,这种类型中可包含多个值,但是在实际去使用枚举的时候只能使用这么多个值中的其中一个。

比如,性别就可以是一种枚举类型,性别的值只能是男或者女(那种不伦不类的就算了,这里不谈)。当你去使用性别这种枚举的时候,要么是男,要么是女,不可能两个都取,也不可能一个都不取。

Rust中的枚举的基本用法:

  • 声明
enum 枚举名 {枚举值1,	//可指定类型也可不指定类型枚举值2,...枚举值n,	
}

最后一个枚举值后面的逗号有没有都不影响,但是官网一般都会加上这个逗号,编译器中格式化代码之后也会自动加。

  • 使用
//未指定枚举值的类型
let 变量名 = 枚举名::枚举值名;
//指定枚举值的类型
let 变量名 = 枚举名::枚举值名(枚举值的实际值)

使用枚举的注意事项:在Rust中枚举不能直接通过if表达式来比较,一般使用match类模式匹配

例子:

enum Sex {	//定义了性别的枚举类型MAN,WOMAN,
}struct Person {	//定义了人的结构体类型name: String,sex: Sex,
}impl Person {	//实现人的info方法fn info(&self) {println!("name: {}", self.name);match self.sex {	//通过模式匹配,不能使用if表达式Sex::MAN => println!("sex: 男"),Sex::WOMAN => println!("sex: 女"),}}
}fn main() {let jack = Person {name: String::from("Jack"),sex: Sex::MAN,};jack.info();
}

结果:

name: jack
sex: 男

我们可以将数据直接放进每一个枚举成员而不是将枚举作为结构体的一部分。

enum Sex {MAN(u8),WOMAN(u8),
}
fn main() {let man = Sex::MAN(1);let woman = Sex::WOMAN(0);match man {//s会获取man中Sex::MAN的值,即1Sex::MAN(s) => println!("这是男人{}", s),_ => println!("这是女人"),}match woman {//使用_则表示不获取值Sex::WOMAN(_) => println!("这是女人"),_ => println!("这是男人"),}
}

以上的例子中可以看出的是两个枚举类型的声明方式不同:

//第一种,没有指定枚举中值的类型
enum Sex {MAN,WOMAN,
}//第二种,指定了枚举中值的类型
//因为结构体也是数据类型的一种
//所以在枚举中当然也可以指定是我们自定义的类型(结构体、元组)
enum Sex {MAN(u8),WOMAN(u8),
}

不管采用哪种方式声明枚举,那么在使用到枚举的时候就按对应的方式进行赋值操作。

枚举和其相对于空值的优势

这一部分会分析一个 Option 的案例,Option 是标准库定义的另一个枚举。Option 类型应用广泛因为它编码了一个非常普遍的场景,即一个值要么有值要么没值

例如,如果请求一个包含项的列表的第一个值,会得到一个值,如果请求一个空的列表,就什么也不会得到。从类型系统的角度来表达这个概念就意味着编译器需要检查是否处理了所有应该处理的情况,这样就可以避免在其他编程语言中非常常见的 bug.

Rust 并没有很多其他语言中有的空值功能。空值Null )是一个值,它代表没有值。在有空值的语言中,变量总是这两种状态之一:空值和非空值。例如C++中的nullptr,Java中的null等。在C语言中的NULL不是没有值而是对0强制转换成void*.所以C语言也可以说是不安全的。

Tony Hoare,null 的发明者,在他 2009 年的演讲 “Null References: The Billion Dollar Mistake” 中曾经说到:

I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

我称之为我十亿美元的错误。当时,我在为一个面向对象语言设计第一个综合性的面向引用的类型系统。我的目标是通过编译器的自动检查来保证所有引用的使用都应该是绝对安全的。不过我未能抵抗住引入一个空引用的诱惑,仅仅是因为它是这么的容易实现。这引发了无数错误、漏洞和系统崩溃,在之后的四十多年中造成了数十亿美元的苦痛和伤害。

而在Rust中这个问题在语言层面就得以解决。因为Rust没有空值的功能,编译都不给通过,保证了一定的安全。

Rust 虽然没有空值,但是它拥有一个可以编码存在或不存在概念的枚举。这个枚举是 Option<T>,而且它定义于标准库中,如下:

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

Option<T> 枚举被包含在 prelude (预处理模块)之中,你不需要将其显式引入作用域。另外,它的成员也是如此,可以不需要 Option:: 前缀来直接使用 SomeNone。即便如此 Option<T> 也仍是常规的枚举,Some(T)None 仍是 Option<T> 的成员。

<T>的操作涉及到泛型,如果学过C++的对这块应该是熟悉的。其中的T就是我们要实际传进去的类型的占位。其中T可以是普通的基本类型也可以是由结构体组成的复杂数据类型。在程序编译阶段就会把我们实际传进去的类型覆盖掉T.泛型就相当于做一个模板,后来者使用的时候就直接按这个模板来做就好。这样做的好处就是节省代码的书写时间。

    let some_number = Some(5);	//此时T是i32类型let some_char = Some('e');	//此时T是char类型let absent_number: Option<i32> = None; //此时T是i32类型,当赋值为None的时候要手动指定变量的类型,

当有一个 Some 值时,我们就知道存在一个值,而这个值保存在 Some 中。当有个 None 值时,在某种意义上,它跟空值具有相同的意义:并没有一个有效的值。

简而言之,因为 Option<T>T(这里 T 可以是任何类型)是不同的类型,编译器不允许像一个肯定有效的值那样使用 Option<T>.

    let x: i8 = 5;let y: Option<i8> = Some(5);let sum = x + y;	//i8类型和Option<i8>不是一个类型。不能直接相加,需要通过转换才可以相加。

报错:

error[E0277]: cannot add `Option<i8>` to `i8`|
5 |     let sum = x + y;|                 ^ no implementation for `i8 + Option<i8>`|					# 没有实现`i8+Option<i8>`= help: the trait `Add<Option<i8>>` is not implemented for `i8`For more information about this error, try `rustc --explain E0277`.
error: could not compile `enums` due to previous error

当在 Rust 中

  • 基本数据类型–>开发者无需做空值检查。
  • Option<T>类型–>开发者需要考虑空值检查。如果出现使用空值,编译器就直接报错。

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

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

Some(T)中 获取T的值:

  • get_or_insert 如果selfNone则将该函数的value赋值给self,返回值是selfSome中的值。
  • insert 不管self是否为None都会把该函数的value赋值给self,返回值是selfSome中的值。

例子:

fn main() {let mut x = None;let y = x.get_or_insert(88);	//x = Some(88), y = 88println!("y = {}", y);println!("x = {:?}", x);let y = x.insert(99);			//x = Some(99), y = 99println!("y = {}", y);println!("x = {:?}", x);
}

get_or_insert_with的坑,使用官方文档的例子会觉得Rust的语法特别奇怪,一时不知道它在干嘛:

let mut x = None;{let y: &mut u32 = x.get_or_insert_with(|| 5);assert_eq!(y, &5);*y = 7;
}assert_eq!(x, Some(7));

其中第4行是亮点。这个||符号可太秀了。。。秀得我头皮发麻。一看以为是逻辑或运算,但是想想也不对,逻辑或运算是双目的,这里是单目的。然后这条代码是把88给了y.

然后就想一下,y为什么能拿到88,然后get_or_insert_with本身是方法(函数),那就可能存在函数嵌套,那就是说||可能是一种匿名函数,测试了一下,发现它就是匿名函数。

这样写你看不懂

let y = x.get_or_insert_with(|| 88);

这样写就看得懂了

let y = x.get_or_insert_with(|| {88});

或者

let y = x.get_or_insert_with(|| {88
});

同样的看以下例子理解它:

fn main() {let mut x = None;let y = x.get_or_insert_with(|| {let ret = 45;	//语句println!("我是匿名函数,我要返回{}", ret);	//语句ret	//表达式 会返回出去});println!("y = {}", y);		//45println!("x = {:?}", x);	//Some(45)
}

还有一个官方案例,但是目前这个函数还不能使用:get_or_insert_default,可以看看官方的说明:Rust官方文档.

if let简单控制流

这个应该在控制流的时候就应该介绍了,但是也没多大关系,其实就是简化match的,就是在特定的情境下相对于match来说少写一些代码。其中特定情境说的就是只模式匹配两种情况,非它即另一些它的情况。下面看例子:

    let config_max = Some(3u8);match config_max {Some(max) => println!("The maximum is configured to be {}", max),_ => (),}

根据上面的代码可以使用if let来简化它:

    let config_max = Some(3u8);if let Some(max) = config_max {println!("The maximum is configured to be {}", max);}

就是少写了_ 的情况。

它的工作方式与 match 相同。使用 if let 意味着编写更少代码。然而,这样会失去 match 强制要求的穷尽性检查。matchif let 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。

换句话说,可以认为 if letmatch 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。

意思就是没什么多大用处。反而还多占了一点开发者脑子的内存。开发者没那么多头发,实际开发的时候想到哪个就用哪个就行了。

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

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

相关文章

Elasticsearch(ES) 基本知识

ES 学习笔记 ES是一个基于Apache的开源索引库Lucene而构建的 开源、分布式、具有RESTful接口的全文搜索引擎, 还是一个分布式文档数据库. 基本概念 index 索引是具有相似结构的文档的集合, 比如可以有一个商品分类索引, 订单索引. 每个索引都要有唯一的名称, 名称要小写, 通…

CleanMyMac X适用于Mac电脑安全的软件

CleanMyMac X 内部包含有很多不同的功能组件&#xff0c;它们需要以各种各样的方式协同合作&#xff0c;这对我而言也是一个新鲜的设计过程。 我注意到&#xff0c;当我们使用造型不完美的对象更加令人愉悦。无菌的空间和完美的物品&#xff0c;甚至可能会让人产生失真感和排斥…

面试突击91:MD5 加密安全吗?

MD5 是 Message Digest Algorithm 的缩写,译为信息摘要算法,它是 Java 语言中使用很广泛的一种加密算法。MD5 可以将任意字符串,通过不可逆的字符串变换算法,生成一个唯一的 MD5 信息摘要,这个信息摘要也就是我们通常所说的 MD5 字符串。那么问题来了,MD5 加密安全吗? 这…

高可用linux 服务器搭建

最原始的服务部署,为单点部署,即直接把服务部署在一个服务器上。如果服务器出现故障,或者服务因为某个异常而挂掉,则服务就会发生中断。单点部署出现故障的概率最高。 后来,出现了网关,比如 nginx kong 等。如下图所示:这样,所有客户请求都会经过网关,再由网关转发到各…

python的opencv操作记录(七)——短时傅里叶变换(stft)

文章目录DCT-傅立叶变换的局限性STFT 短时傅里叶变换从另一个角度来理解图像的“时域”数据看看fs和t这两个参数再看看怎么划分窗口最后看另外两个出参Zxx返回结构图像的stftDCT-傅立叶变换的局限性 接上一篇DCT的文章&#xff0c;DCT只提取了整个信号域的频率信息&#xff0c…

Python——HTTP代理 Proxy

大佬勿喷 链接: https://pan.baidu.com/s/1Wm0JepiZa9iPn4WoQU3Qdg 提取码: p09g

【计算机毕业设计】基于微信小程序的校园生活服务平台

提供了一些今年最新计算机毕业设计源代码、文档及帮助指导&#xff0c;公众号&#xff1a;一点毕设&#xff0c;领取更多毕设资料。 一.课题概述 随着互联网时代的到来&#xff0c;移动端应用的发展十分迅猛&#xff0c;校园服务类应用 也是不计其数。但大多功能单一&#xf…

Word处理控件Aspose.Words功能演示:使用 Java 将 RTF 转换为 PDF

RTF 格式由 Microsoft 引入&#xff0c;用于创建富文本文档。RTF 的互操作性使得在不同的 Microsoft 产品以及异构操作系统之间交换内容成为可能。但是&#xff0c;有时可能需要将 RTF 转换为 PDF 以用于打印、共享或其他目的。因此&#xff0c;本文介绍了 如何使用 Java 以编程…

干净的代码——一种实用的方法

在对干净代码进行了一些讨论之后&#xff0c;我决定在一篇文章中总结最重要的事情。因为网上有很多关于清洁代码的帖子和信息&#xff0c;我认为一篇新的文章谈论它只是解释一些原则是不值得的。在本文中&#xff0c;我将尝试为您提供清洁代码的实用方法。我不会深入理论&#…

海康大华等录像机、摄像头无法通过GB28181注册到LiveGBS国标平台问题排查方法

LiveGBS常见问题-海康大华宇视华为NVR摄像头无法注册到平台国标平台看不到设备的时候如何抓包及排查1、设备注册后查看不到1.1、防火墙排查1.2、端口排查1.3、IP地址排查1.4、设备TCP/IP配置排查1.5、设备多网卡排查1.6、设备接入配置参数排查1.7、设备尝试修改本地SIP端口1.8、…

Biotin-PEG-Alk,Biotin-PEG-Alkyne,生物素-聚乙二醇-炔烃科研用试剂供应

An English name&#xff1a;Biotin-PEG-Alk&#xff0c;Biotin-PEG-Alkyne Chinese name&#xff1a;生物素-聚乙二醇-炔烃 Item no&#xff1a;X-GF-0267-10k CAS&#xff1a;N/A Formula&#xff1a;N/A MW&#xff1a;Biotin-PEG1-Alkyne/Biotin-PEG2-Alkyne/Biotin-P…

uni-app实战之单击菜单发布->H5的Promise 化在工程项目的实战演练项目心得

H5 开发注意 H5发布到服务器注意&#xff1a; 配置发布后的路径&#xff08;在网站的根目录中发布是可选的&#xff09;&#xff0c;例如发布网站的路径为www.xxx Com/html5&#xff0c;在manifest中编辑json文件中的h5节点&#xff0c;并将base属性添加到路由器下的html5 单…

基于PHP的蔬菜价格查询管理系统设计与实现

目 录 1 引言 1 1.1 课题背景与意义 1 1.2 课题现状与可研究性 1 1.3 本论文研究内容和结构安排 1 2 系统基础概述 2 2.1 软件开发环境 2 2.2 L&#xff0c;Linux操作系统 2 2.3 A&#xff0c;Apache服务器 2 2.4 M&#xff0c;Mysql数据库 3 2.5 P&#xff0c;PHP语言 3 2.5.1…

EXCEL表格-VLOOKUP多对一结果匹配方法(通配符)

❤关注我&#xff0c;不迷路❤ 点击进入EXCEL综合应用场景专栏 在实际使用场景中&#xff0c;通过一个值去匹配另一个值的案例很常见&#xff0c;比如一份学校的信息表&#xff0c;通过姓名查找班级、家长姓名等&#xff0c;均用VLOOKUP函数可以实现&#xff0c;正向查找、逆…

【Coel.学习笔记】莫比乌斯反演

冷知识:百度百科里甚至没有对反演的准确定义……闲话 记得在差不多一年前写扩展欧拉定理的时候我提了一句这周终于把古代猪文搞定了,数论这块的内容就只剩个博弈论了 别提莫比乌斯反演之类的东西,我不想搞甚至刚开始写的时候还笔误把莫反写成了莫队…… 转眼一年过去了,来填…

leetcode 123买卖股票的最佳时机III

买卖股票的最佳时机III 动态规划-分两小组分别计算&#xff08;超时&#xff09; class Solution { public:int partprofit( vector<int>& prices , int start , int end ){if((end-start)<1) return 0;vector<int> dp(end - start , 0);int min prices[s…

视觉检测工作台设计

目 录 摘 要 I Abstract II 第1章 引言 1 1.1研究背景及意义 1 1.2国内外研究现状 2 第2章 总体方案的确定 4 2.1方案拟定 4 2.1.1机械结构 4 2.1. 2控制工艺要求 5 2.1. 3总体方案 5 2.2 设计参数 7 第3章 视觉检测工作台机械系统设计 8 3.1 X-Y数控工作台总体方案的确定 8 3.…

微信公众号查题搜题平台

微信公众号查题搜题平台 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#xf…

物联网、区块链、元宇宙和虚拟数字人离普罗大众有多远?

首先&#xff0c;我们最早理解的数字人就是数字虚拟的一个假人&#xff0c;可能看起来很像二次元玩偶的样子。今天我觉得数字人是一种虚拟的数字身份&#xff0c;无所谓你的形象是仿真或是任何形象&#xff0c;包括你在现实中无法实现的形象&#xff0c;你在梦想中所渴望的概念…

【数据结构与算法分析】0基础带你学数据结构与算法分析01--基础数学知识

&#x1f353;个人主页&#xff1a;个人主页 &#x1f4ac;推荐一款模拟面试、刷题神器&#xff0c;从基础到大厂面试题&#xff1a;点击跳转进入网站 &#x1f4e9;如果你想学习算法&#xff0c;以及一些语言基础的知识&#xff0c;那就来这里&#xff1a;​​​​刷题网站 跟…