0x00 回顾
上篇文章咱们介绍了结构体,结构体类型是一种自定义的数据类型,当然也可以把 多个类型组合在一起成为新的类型。本篇文章介绍另一种自定义数据类型——枚举(enum)。一起来看看它和结构体又有什么区别吧
0x01 定义
枚举类型是一种自定义的数据类型,使用 **enum **关键字 + 自定义的命名 + 枚举值来定义。我们通过使用 枚举名 :: 枚举值 来访问枚举的值。使用场景:当一个参数可能存在多个取值范围时,我们定义成枚举类型来控制变量的取值范围,从而防止输入非法值。示例代码如下:
// 枚举示例代码// 声明一个星期的枚举类型,只有7天,周一到周日。 enum Week { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
0x02 C 式枚举
上面的示例代码,就是Rust中最简单的枚举类型了,同 C 语言 一模一样。在内存中,C 式枚举的值存储为整数,默认从0开始,后面的值依次 +1。如果想更改某个枚举的值,需要告诉它用什么整数。示例代码如下:
// 为了可以格式化打印枚举值,需要加入下面这行代码,具体有关 trait 的知识将在后面介绍。 #[derive(Debug)]enum Week { Mon, Tue, Wed, Thu = 300, Fri, Sat, Sun}fn main() { // 打印枚举类型 println!("{:?}", Week::Wed); // 打印枚举的值 println!("{}", Week::Wed as i32); println!("{}", Week::Mon as i32); // 由于Thu 赋值维 300, 则后面的值依次+1 println!("{}", Week::Fri as i32); } // 输出结果: // Wed // 2 // 0 // 301
0x03 包含数据的枚举
在 Rust 中,枚举的值存在 3 种变体,分别对应前两篇文章介绍的 3 种结构体。没有数据的变体对应类基元结构体,元组变体的写法和作用同元组结构体一样,结构体变体则存在花括号和命名字段。在一个枚举里可以同时存在3 种变体。
#[derive(Debug)] enum Color { // 定义色值,参数分别表示16进制颜色代码,R,G,B White(String, u8, u8, u8), Red, Black { code: String, r: u8, g: u8, b: u8 }, } fn main() { let white = Color::White(String::from("#FFFFFF"), 255, 255, 255); let red = Color::Red; let black = Color::Black { code: String::from("#000000"), r: 0, g: 0, b: 0 }; println!("{:?}", white); println!("{:?}", red); println!("{:?}", black); // 输出结果: // White("#FFFFFF", 255, 255, 255) // Red // Black { code: "#000000", r: 0, g: 0, b: 0 } }
0x04 枚举的内存布局
我们以 Week 为例,来看下枚举在内存中的布局吧。废话不多说,先上图,后解释。
不知道大家能不能看懂呢。首先如果一个枚举类型存在多个枚举值,那么它会为每个枚举值分配一个标签,从0开始计数,占1字节。我们每个枚举值,还有3个u8类型的值,每个类型占1字节。由于对齐原因,留空4字节,对齐8字节(有关Rust内存对齐的问题,我找时间单独一篇文章介绍)。最后String占24字节,关于String的内存模型,我已经在前面讲过了。Week 总共占 32 字节。
最后,我们通过编译器验证下内存数据。如下图:
另外,如果一个枚举类型仅存在一个枚举值,那么他不会存在占一个字节的标签。大家可通过编译器自行验证。我在这里就不做赘述了。
注:上面的内存模型是以 Rust 1.62.1 版本为基础的。Rust 后续依然可能会对枚举的内存作出优化。
0x05 小结
枚举类型到这里就介绍完了,其实枚举在任何项目中都是很常见的。本篇文章主要介绍了枚举的定义和内存布局,使用枚举的是为了必须保证安全的访问数据,在某些场景下大概率会同模式匹配一起使用。
介绍了枚举类型之后,Rust 常用的数据类型已经介绍完了。之后的文章将进入 Rust 中级阶段,了解 Rust 的泛型,Trait,所有权指针等等。接下来的文章将是真正揭开 Rust 神秘面纱的时刻。