Rust 探索(八)—— 枚举与模式匹配(上)
Rust 探索(八)—— 枚举与模式匹配(上)
¶1. 枚举类型
枚举类型的特点就是列举所有可能的值来定义一个类型
就比方说交通信号灯的颜色,总共有红、黄、绿3种可能,那么作为灯的颜色这个类型,有3个枚举变体,分别是RED(红)、YELLOW(黄)、GREEN(绿)
¶2. 定义枚举
使用enum
关键字定义枚举类型
1 | enum Signal { // 使用enum定义枚举 |
如上所示,Signal就是枚举类的名称,对比着结构体的内容来看,RED、YELLOW、GREEN是3个成员,不过在枚举中,它们称为变体
另外,这三个变体同属于Signal这一个类型,红、黄、绿都是灯的颜色,只不过呈现的形式不同
¶3. 枚举值
1 | let green = Signal::GREEN; |
枚举类和变体之间使用::
分隔,这里的green和red相当于是对应枚举变体的实例了
注意,这两个变量对应的是同一类型,正如Rust编译器所推导的一样
因此,如果需要定义函数处理它们,只需定义Signal类型的参数
1 | fn decide(signal: Signal) { |
1 | decide(green); // 同一处理变体 |
¶4. 枚举vs结构体
面对正确的场景使用正确的结构总是能发挥最大的优势
我们知道,红灯停、绿灯行、黄灯提醒,此时如果我们想要把灯的颜色和其代表的信息一同打包,应该如何选择呢?
如果使用结构体,那么可能会这样定义
1 | struct SignalInfo { |
1 | let green = SignalInfo { // 以绿灯为例 |
似乎没有很复杂,逻辑清晰,color字段接收颜色,info字段放提示信息,使用的时候实例化类型
但是,作为该领域的霸主,枚举举手表示它有更好的方案
1 | enum Signal { |
这就好比结构体是跟别人借车放东西,而枚举是私家车,有东西直接打包放后备箱,明显更灵活
1 | let green = Signal::GREEN(String::from("行")); |
直观感受就是简短
并且,更加强大的一点是枚举后面关联的类型并不需要一样
1 | enum Signal { |
私家车嘛,想怎么放是你的自由,用别人的车可是事先要说清楚的,临时变卦可不行
¶5. 枚举方法
枚举的方法定义其实和结构体非常相似,都是使用impl
关键字
1 | impl Signal { |
同样地,使用impl
创建一个与枚举同名的块,&self
也是代表了实例
1 | let red = Signal::RED(true); |
相应地,如果定义关联函数,也是使用枚举名称加上::
调用
1 | impl Signal { |
1 | Signal::print(); |
¶6. Option 枚举
Option
枚举是Rust中一个很重要的存在,这种重要性需要从一个空值的概念说起
现代的许多编程语言都会有一个所谓的空值的概念,在Java中就是大名鼎鼎的null
,并且很自然地就会联想到令人头疼的NullPointerException
我们通常的程序,都是需要有特定类型的值,用作书写业务逻辑,但是null
表示一种无效的场景,用于和正确的逻辑赋值区别开来,可能是没有初始化或者传值不合法等等,这种null
是不能直接拿来使用的,尤其是在其上调用函数,立马会导致程序崩溃
在代码达到一定规模时,这种可能出现的null
会遍布程序当中,不知什么时候就会被触发,即使编写代码时,一直注意判断也很难保证安全,于是后来出现的一些语言针对空安全做了很多专项处理,比如说Kotlin、TypeScript等,Rust甚至索性不支持空值
不支持空值是指不会拿给你用,但是保留了一个概念,用以表示这种无效或缺失的情形
1 | pub enum Option<T> { |
标准库中定义了Option
枚举,它有两个枚举变体:None
和Some
None
表示的就是所谓值无效或者缺失的含义,而Some
表示一个T
的类型,这里的T
表示的泛型的概念,就如同Java的泛型和C++的模板,意味着Some
可以表示各种类型的值,这也是正常情况下我们需要使用的有效值
1 | let valid_value = Some("有效值"); // 直接赋值,推断出对应类型为字符串切片 |
可以直接使用Some
和None
,但是**None
必须显式指定类型**,否则编译器无法推断
好像看起来,None
和所谓null
没什么区别,但是别忘了,Some
和None
对应的可是同一个枚举类型
Option<T>
表明T
可能无效,也可能是T
,这两种情况你必须都要考虑,不然你通不过编译,这就和Kotlin的可空类型差不多
通常的处理可以搭配上match
表达式使用
¶7. 控制流运算符 match
1 | enum Waste { |
1 | fn classify(waste: Waste) -> String { |
match
后面跟一个表达式,便是上面的waste字段,这是带匹配的条件,接下来会依次与下面的各个变体进行匹配,这些候选的模式采用,
分隔开来,并且使用=>
将对应的值和代码逻辑关联起来,作为一个整体,类似于如果…那么…,与Java当中的开关语句switch...case
接近
如果=>
的代码有多行语句,那么还需要加上{}
1 | fn classify(waste: Waste) -> String { |