初识枚举

此篇假定读者了解枚举的作用,如你还没有接触枚举或者对枚举的应用不太了解的话,建议先补完基础后再看

另外枚举涉及到Rust的一个特别功能——模式匹配,为了更好理解,请仔细看完本章的#更近一步这一节。

使用enum声明定义枚举


枚举的命名规范和结构体命名规范一样,使用大驼峰命名法。

一个简单枚举定义如下:

1
2
3
4
enum Gender {
Male,
Female,
}

更进一步


Rust的枚举并不局限于表示类型,还可以嵌入结构体和另一个枚举(如果嵌套自己会导致无法确定类型大小):

1
2
3
4
5
6
7
8
9
enum Signal {
None,
Num(i128),
Bool(bool),
Info {
id: u64,
age: u8,
}
}

使用match匹配


说完定义枚举,接下来该介绍如何使用枚举了。

回顾一下,我在控制流一章中只介绍了match值匹配和条件匹配,现在来说一下类型匹配。

一个简单的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum Num {
One,
Two,
Three,
}

fn convert(num: Num) -> u8 {
return match num {
// num为Num::One时,返回值1
Num::One => 1,
// num为Num::Two时,返回值1
Num::Two => 2,
// num为Num::Three时,返回值1
Num::Three => 3,
};

// 错误写法
// if num == Num::One { 1 }
// else ...
// 报错原因是Num未实现判断相等的`trait`(超前知识)
}

fn main() {
let num = Num::One;
let num = convert(num); // num = 1;
}

而对于嵌入结构体的枚举,可以这样使用match

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
enum Signal {
None,
Bool(bool),
Info {
id: u64,
age: u8,
}
}

fn pln(sig: Signal) {
match sig {
// 匹配语句后的语句可以是一个函数
Signal::None => println!("None!"),
// bl是一个`bool`
Signal::Bool(bl) => println!("{}", bl),
// 与初始化结构体时相反,匹配时会将字段值赋值给目标值,例如Info中的id给iid进行了赋值。
// 同样,当变量名与字段名相同时,可以省略掉后面的: 变量名。
// 另外,这些变量均不需要手动声明。
Signal::Info{ id: iid, age } => {
println!("id={}\nage={}", iid, age);
}
}
}

fn main() {
let arr = [
Signal::None,
Signal::Bool(true),
Signal::Info{ id: 0, age: 18 },
];

for sig in arr {
pln(sig);
}
}

输出:

1
2
3
None!
true
id=0, age=18

上面对枚举Num使用match进行的操作又被称为匹配。

if let简洁匹配


match匹配需处理所有可能的情况,如果只需匹配特定的情况,则可以使用if let简洁匹配。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Signal {
None,
Bool(bool),
Info {
id: u64,
age: u8,
}
}

fn pln_bool(sig: Signal) {
if let Signal::Bool(bl) = sig {
// 当`sig`为Signal::Bool(bool)型时,执行这里的语句
println!("这个信号为Signal::Bool(bool), 其中bool值为{}", bl);
}
}

while let循环匹配


if let类似,略。

let匹配

使用let声明并定义变量时,可以使用匹配定义语法(非官方术语):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let nu = (12, 32.);
// 下面与let (nu1: i32, nu2) = nu等价
let (nu1, nu2: f64) = nu; // 可以省略类型声明

let nu = [12, 34];
let [nu1, nu2] = nu;

struct Nu {
t1: i32,
t2: i64
}
let nu = Nu{ t1: 12, t2: 34 };
// 该方法有一个缺点是无法指定类型,变量类型必须与对应字段类型相同
let Nu{ t1: nu1, t2: nu2} = nu;

C风格用法


同C一样,Rust的普通枚举(高级枚举未测试)也有辨别值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 辨别值从0开始
enum Num {
Zero, // 辨别值为0
One, // 辨别值为1
Two, // 辨别值为2
Three, // 辨别值为3
Five, // 注意这里辨别值为4
}

// 可以显式指定辨别值
enum Num2 {
Zero, // 辨别值为0
// 在显式指定辨别值前的字段遵循上面的规则从0开始
Two = 2, // 辨别值为2
One = 1, // ...
Three = 3,
}

与C不同,要使用辨别值必须进行类型强转:

1
2
let t1 = Num2::Two as i32;
// let t2: i32 = Num2::One; //此句无法编译