枚举
语法
枚举 :
enum
标识符 GenericParams? WhereClause?{
EnumItems?}
EnumItems :
EnumItem (,
EnumItem )*,
?EnumItem :
OuterAttribute* Visibility?
标识符 ( EnumItemTuple | EnumItemStruct )? EnumItemDiscriminant?EnumItemTuple :
(
TupleFields?)
EnumItemStruct :
{
StructFields?}
EnumItemDiscriminant :
=
Expression
枚举,也称为 enum,是对命名 枚举类型 以及一组构造器的同时定义,这些构造器可用于创建或模式匹配相应枚举类型的值。
枚举使用关键字 enum
声明。
enum
声明在它所在的模块或块的 类型命名空间 中定义了枚举类型。
一个 enum
项及其用法的示例
#![allow(unused)] fn main() { enum Animal { Dog, Cat, } let mut a: Animal = Animal::Dog; a = Animal::Cat; }
枚举构造器可以具有命名或未命名的字段
#![allow(unused)] fn main() { enum Animal { Dog(String, f64), Cat { name: String, weight: f64 }, } let mut a: Animal = Animal::Dog("Cocoa".to_string(), 37.2); a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; }
在这个例子中,Cat
是一个 结构体式枚举变体,而 Dog
仅被称为枚举变体。
如果一个枚举的所有构造器都不包含字段,则称为无字段枚举。 例如,这是一个无字段枚举
#![allow(unused)] fn main() { enum Fieldless { Tuple(), Struct{}, Unit, } }
如果一个无字段枚举仅包含单元变体,则该枚举称为单元枚举。 例如
#![allow(unused)] fn main() { enum Enum { Foo = 3, Bar = 2, Baz = 1, } }
变体构造器类似于 struct 定义,并且可以通过枚举名称的路径引用,包括在 use 声明 中。
每个变体都在 类型命名空间 中定义其类型,尽管该类型不能用作类型说明符。 元组式和单元式变体还在 值命名空间 中定义一个构造器。
结构体式变体可以使用 结构体表达式 实例化。
单元式变体可以使用 路径表达式 或 结构体表达式 实例化。 例如
#![allow(unused)] fn main() { enum Examples { UnitLike, TupleLike(i32), StructLike { value: i32 }, } use Examples::*; // Creates aliases to all variants. let x = UnitLike; // Path expression of the const item. let x = UnitLike {}; // Struct expression. let y = TupleLike(123); // Call expression. let y = TupleLike { 0: 123 }; // Struct expression using integer field names. let z = StructLike { value: 123 }; // Struct expression. }
判别式
每个枚举实例都有一个判别式:一个与之逻辑关联的整数,用于确定它持有的变体。
在 Rust
表示 下,判别式被解释为 isize
值。 但是,编译器可以在其实际内存布局中使用更小的类型(或区分变体的其他方法)。
分配判别式值
显式判别式
在两种情况下,可以通过在变体名称后跟 =
和一个 常量表达式 来显式设置变体的判别式
- 如果枚举是“单元枚举”。
-
如果使用了 原始表示。 例如
#![allow(unused)] fn main() { #[repr(u8)] enum Enum { Unit = 3, Tuple(u16), Struct { a: u8, b: u16, } = 1, } }
隐式判别式
如果未指定变体的判别式,则将其设置为比声明中前一个变体的判别式高一。 如果声明中第一个变体的判别式未指定,则将其设置为零。
#![allow(unused)] fn main() { enum Foo { Bar, // 0 Baz = 123, // 123 Quux, // 124 } let baz_discriminant = Foo::Baz as u32; assert_eq!(baz_discriminant, 123); }
限制
当两个变体共享相同的判别式时,这是一个错误。
#![allow(unused)] fn main() { enum SharedDiscriminantError { SharedA = 1, SharedB = 1 } enum SharedDiscriminantError2 { Zero, // 0 One, // 1 OneToo = 1 // 1 (collision with previous!) } }
当未指定的判别式的前一个判别式是判别式大小的最大值时,这也是一个错误。
#![allow(unused)] fn main() { #[repr(u8)] enum OverflowingDiscriminantError { Max = 255, MaxPlusOne // Would be 256, but that overflows the enum. } #[repr(u8)] enum OverflowingDiscriminantError2 { MaxMinusOne = 254, // 254 Max, // 255 MaxPlusOne // Would be 256, but that overflows the enum. } }
访问判别式
通过 mem::discriminant
std::mem::discriminant
返回对可以比较的枚举值的判别式的不透明引用。 这不能用于获取判别式的值。
类型转换
如果一个枚举是 单元枚举(没有元组和结构体变体),那么它的判别式可以直接通过 数值转换 访问; 例如
#![allow(unused)] fn main() { enum Enum { Foo, Bar, Baz, } assert_eq!(0, Enum::Foo as isize); assert_eq!(1, Enum::Bar as isize); assert_eq!(2, Enum::Baz as isize); }
无字段枚举 如果它们没有显式判别式,或者只有单元变体是显式的情况下,可以进行类型转换。
#![allow(unused)] fn main() { enum Fieldless { Tuple(), Struct{}, Unit, } assert_eq!(0, Fieldless::Tuple() as isize); assert_eq!(1, Fieldless::Struct{} as isize); assert_eq!(2, Fieldless::Unit as isize); #[repr(u8)] enum FieldlessWithDiscrimants { First = 10, Tuple(), Second = 20, Struct{}, Unit, } assert_eq!(10, FieldlessWithDiscrimants::First as u8); assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8); assert_eq!(20, FieldlessWithDiscrimants::Second as u8); assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8); assert_eq!(22, FieldlessWithDiscrimants::Unit as u8); }
指针类型转换
如果枚举指定了 原始表示,则可以通过不安全指针类型转换可靠地访问判别式
#![allow(unused)] fn main() { #[repr(u8)] enum Enum { Unit, Tuple(bool), Struct{a: bool}, } impl Enum { fn discriminant(&self) -> u8 { unsafe { *(self as *const Self as *const u8) } } } let unit_like = Enum::Unit; let tuple_like = Enum::Tuple(true); let struct_like = Enum::Struct{a: false}; assert_eq!(0, unit_like.discriminant()); assert_eq!(1, tuple_like.discriminant()); assert_eq!(2, struct_like.discriminant()); }
零变体枚举
具有零个变体的枚举被称为零变体枚举。 由于它们没有有效值,因此无法实例化。
#![allow(unused)] fn main() { enum ZeroVariants {} }
零变体枚举等价于 never 类型,但它们不能被强制转换为其他类型。
#![allow(unused)] fn main() { enum ZeroVariants {} let x: ZeroVariants = panic!(); let y: u32 = x; // mismatched type error }
变体可见性
枚举变体在语法上允许 Visibility 注解,但是当验证枚举时,这会被拒绝。 这允许项目在使用它们的不同上下文中以统一的语法进行解析。
#![allow(unused)] fn main() { macro_rules! mac_variant { ($vis:vis $name:ident) => { enum $name { $vis Unit, $vis Tuple(u8, u16), $vis Struct { f: u8 }, } } } // Empty `vis` is allowed. mac_variant! { E } // This is allowed, since it is removed before being validated. #[cfg(FALSE)] enum E { pub U, pub(crate) T(u8), pub(super) T { f: String } } }