8.2 Vector + Enum的应用
8.2.0. 本章内容
第八章主要讲的是 Rust 中常见的集合。Rust 提供了很多集合类型的数据结构,这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。
第八章中的集合是存储在堆内存上而非栈内存上的,这也意味着这些集合的数据大小无需在编译时就确定,在运行时它们可以动态地变大或变小。
本章主要会讲三种集合:Vector、String 和 HashMap。
8.2.1. Vector 和 enum 如何互补
虽然 Vector 可以动态地变大或变小,但是它里面元素的数据类型仍然必须相同。但有的时候我们需要在堆内存上存储不同类型的数据,那这种情况怎么办呢?
还记得 6.1. 定义枚举 中介绍的枚举类型吗?枚举的变体可以附加数据,而且这些附加的数据可以是不同类型。最主要的是,枚举类型的变体都定义在同一个枚举类型下,也就是说所有的变体都是同一个类型,因此可以被存储到 Vector 中。
这样就可以通过枚举,让 Vector 能够存储不同数据类型的数据。
8.2.2. Vector + enum
来看一个实际使用 Vector + 枚举类型的例子:
enum SpreadSheetCell {
Int(i32),
Float(f64),
Text(String),
}
fn main() {
let row = vec![
SpreadSheetCell::Int(5567),
SpreadSheetCell::Text("up up".to_string()),
SpreadSheetCell::Float(114.514),
];
}
这个例子模拟了 Excel 单元格的行为:单元格内只能存储整数、浮点数或字符串其中之一。所以我们定义了 SpreadSheetCell 这个枚举类型,拥有 3 个变体,分别用于存储整数(Int)、浮点数(Float)和字符串(Text)。
在 main 函数中,声明了变量 row 用于存储一行的单元格。因为一行的单元格数量不确定,所以需要 Vector 来存储。在这个例子里初始化时有三个单元格:第一个存储整数 5567,第二个放了字符串 "up up",第三个放了浮点数 114.514。
通过这个例子,我们可以看到:通过使用可附加数据的枚举类型,就可以变相地在 Vector 里存放不同类型的数据。
那么 Rust 为什么在编译时就需要知道 Vector 里元素的类型呢?因为这样 Rust 才能确定堆内存上到底需要多少内存来容纳这个 Vector。除此之外,如果允许在 Vector 上存储不同类型的元素,那么在对元素进行批量操作时,有些操作可能对某些类型合法、对另一些类型不合法,程序就会出错。而这种枚举类型配合 match 表达式的方式,使得 Rust 能在编译时提前知晓所有可能情况,在运行时就可以正确处理了。
在这个例子上,Vector 确实实现了存储不同的数据类型,但前提是我们必须确切知道可能有哪些数据类型(也就是集合是详尽的)。否则,如果这个类型有无限种可能(或者说不详尽),那么使用枚举也没有办法,连枚举都定义不出来。针对这种情况,Rust 提供了 trait,但这个得等到后面再讲。