前言#
昨天我们学了struct的method以及关联函数(associate functions),那么第五章就学完了。
今天继续往下学第六章
枚举以及模式匹配#
相信各位学java或者ts的时候就学过enum了吧,也应该知道枚举使用来干啥的,比如:
enum gender {
man = '男'
female = '女'
} 性别这种肯定(?)只有两种值的场景就最好用来做枚举了,这里就不多说了。
我们来看下rust中枚举类型如何定义。
定义枚举#
和别的语言一样,定义枚举类型的关键字也是enum。官方给的例子是ip类型,只有v4和v6两个变体(variant)
enum IpAddrKind {
V4,
V6,
} 而获取枚举的值方式则和别的语言不一样
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;用法和访问关联函数一样,用::
rust中的enum可不止这点功能,我们来看一个例子
enum IpAddrKind {
V4,
V6,
}
struct IpAddr {
kind: IpAddrKind,
address: String,
}
let home = IpAddr {
kind: IpAddrKind::V4,
address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
kind: IpAddrKind::V6,
address: String::from("::1"),
};我们定义了一个枚举和一个结构体,其中结构体关联了ip类型和地址两个属性。这样做是非常合理非常普遍的,但是rust还给我们的enum加了点东西,让它变得更方便。直接来看下改动后
enum IpAddr {
V4(String),
V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));肉眼可见的代码量减少。
我们来看下里面改动了什么
IpAddr这个enum中,v4和v6后面多了个圆括号(String)。- 声明
home的时候::v4后面圆括号传入了地址。
意思是这个v4和v6都可以传入一个关联的字符串类型数据作为这个类型的值。
我们甚至没有用到struct就把类型和地址都定义好了。
这可能比较难理解,我们可以再搞个例子
#[derive(Debug)]
enum PixelPart {
R(u8),
G(u8),
B(u8),
A(f64),
}
fn main () {
const WHITE: (PixelPart, PixelPart, PixelPart, PixelPart) = (PixelPart::R(255), PixelPart::G(255), PixelPart::B(255), PixelPart::A(1.0));
dbg!(WHITE);
}
我们定义了一个像素的枚举,然后赋值给WHILTE的常量(一般不这样赋值。。。)。
这个例子应该就很好理解了。
一个像素只有四种成分,每种成分可以关联一个数值用于表示该成分的色值,这里赋值(255, 255, 255, 1.0)表示一个白色的像素。
另外enum中的变体可以接收多个数据,比如
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1); 注意并不是接收一个元组,而是多个关联数据,为什么这么说呢?因为这些个数据并不需要保持相同类型,而元组tuple前面学过是得保持相同类型的。

可以看到并没有报错。
当然不知这一种,你甚至可以直接传入一个struct到里面,这样就不用传所有参数了。
实际上传入一个struct才是最合适的,因为都是有关联的参数对于一个枚举类型来说。
当然,和结构体一样,也是可以给enum定义method的。这里就不多说了。
Option Enum#
在其它语言中存在null这个值,表示空值。注意,是空值,而不是undefined。而在rust中是没有null这个类型的。
rust的开发者在2009年的报告中把null称为“价值数十亿美元的错误”。为啥会这么的说呢?还不是因为"安全"。。。
null也是一种值,他和non-null其实没什么区别,只是值不同的问题。
那么如果把null用来计算。。。。那自然就会出问题。。这种问题在js中还挺多的,毕竟是一个边解析边执行的语言。
不过rust虽然没有null这个类型,但是这个概念还是很有用的,毕竟有的东西会在某个状态中或者某段时间段中变成不存在或者无效。
那这里就有个问题了,rust中的这个null概念到底有啥不同?我个人是这样理解的:你可以用来判断,但不可用于计算(类似计算的其他情况),在编译的时候如果有这样场景会直接报错,直到这个值不再是无效状态。
那么要怎么用呢?
Option是一个枚举,有两个变体:None和Some。也就是有效和无效的概念。
enum Option<T> {
None,
Some(T),
}注意这里的T是一种泛型,大家应该都清楚是啥了吧。。rust中的泛型会在后面讲。
由于Some和None很常用,所以rust提供了语法糖,让你不需要添加Option::这个前缀。比如:
let some_number = Some(5);
let some_char = Some('e');
let absent_number: Option<i32> = None; 
类型(T)不一定要传,会自动推断。
另外这个T和Option不是一个类型,比如
let x: i8 = 5;
let y: Option<i8> = Some(5);
let sum = x + y;
他俩并不能相加,因为一个是i8类型,另一个是Option类型。
这也就是说,虽然有值,但它不能用于计算,这也就从某种意义上杜绝了null用于计算的场景。
总结#
Option这个概念还是比较深澳的,可能我的理解有问题,如果有不同看法麻烦评论里说一下,谢谢~
参考#
- ^rust-enum https://doc.rust-lang.org/book/ch06-00-enums.html#enums-and-pattern-matching
- ^rust-define-enum https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html#defining-an-enum
编辑于 2022-12-09 15:10・IP 属地广东
