在很多编程语言中, 枚举型主要是为了限制值域. 一个函数的参数类型如果设置成枚举型, 就能通过类型的限制, 让开发者更不容易误用. 除此之外, 枚举型实际上也是类型理论中的 Sum 类型, 即它表示了多个 (子) 类型的集合. 这一点在 C++ 这种枚举型原生不关联其他数据的语言中可能感受不强, 但是在 Rust, Haskell 等语言中, 尤其是在还支持模式匹配的情况下, 可能感受就比较强. 比如以下代码:

1
2
3
4
5
6
7
8
9
10
11
enum Result<T, E> {
Ok(T),
Err(E),
}

let some_result: Result<u8, u8> = ...;

match some_result {
Ok(value) => println!("Ok with value: {}", value),
Err(error) => println!("Error with value: {}", error),
}

上面的代码中, some_result 这个变量的类型, 可以理解成要么是 Ok, 要么是 Err.

数据存储

由于一个枚举型变量的具体’子类型’无法完全在编译时判断, 所以在存储上处理 ‘payload’ 之外还需要一些信息来帮助判断到底是什么子类型. 比如在 C++ 中, 一个 union 的结构体一般都会在配上一个 enum 来表示 union 结构体中到底包含了什么数据. 在 Rust 中也是如此, 同时, 就像 union 一样, 一个 enum 值, 不管是什么子类型, 都需要分配最大可能子类型所需要的空间.

空指针优化

所谓空指针优化实际上可以理解为是某种优化的特殊形式. 如果子类型之间可以通过数据本身的特征区分, 那就不需要在存储多余的信息来区分了. 对于空指针优化这种具体情况, 我们用一个例子来说明:

1
2
3
4
enum Foo {
A,
B(SomeNonNullPointer),
}

对于 Foo 的 B 子类型, 因为我们知道包含的指针一定不是 null, 所以如果我们发现内存中全是 0, 我们就知道是 A. 这个看上去好像不是一个很有用的场景, 但是在 Rust 中, 对于 &, &mut, Box, Rc, Arc, Vec 和其他很多重要的类型, 如果把他们放在 Option 中, 就不需要额外的存储消耗.