diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md index 428f053ef..1b91188e8 100644 --- a/src/expressions/operator-expr.md +++ b/src/expressions/operator-expr.md @@ -338,7 +338,7 @@ well as the following additional casts. Here `*T` means either `*const T` or | Type of `e` | `U` | Cast performed by `e as U` | |-----------------------|-----------------------|----------------------------------| | Integer or Float type | Integer or Float type | Numeric cast | -| C-like enum | Integer type | Enum cast | +| Field-less enum | Integer type | Enum cast | | `bool` or `char` | Integer type | Primitive to integer cast | | `u8` | `char` | `u8` to `char` cast | | `*T` | `*V` where `V: Sized` \* | Pointer to pointer cast | diff --git a/src/items/enumerations.md b/src/items/enumerations.md index 258795503..f94c06d19 100644 --- a/src/items/enumerations.md +++ b/src/items/enumerations.md @@ -13,8 +13,8 @@ > > _EnumItem_ :\ >    _OuterAttribute_\*\ ->    [IDENTIFIER] ( _EnumItemTuple_ | _EnumItemStruct_ -> | _EnumItemDiscriminant_ )? +>    [IDENTIFIER] ( _EnumItemTuple_ | _EnumItemStruct_ )? +> _EnumItemDiscriminant_? > > _EnumItemTuple_ :\ >    `(` [_TupleFields_]? `)` @@ -56,22 +56,68 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; ``` In this example, `Cat` is a _struct-like enum variant_, whereas `Dog` is simply -called an enum variant. Each enum instance has a _discriminant_ which is an -integer associated to it that is used to determine which variant it holds. An -opaque reference to this discriminant can be obtained with the -[`mem::discriminant`] function. +called an enum variant. -## Custom Discriminant Values for Field-Less Enumerations +## Discriminants -If there is no data attached to *any* of the variants of an enumeration, -then the discriminant can be directly chosen and accessed. +Each enum instance has a _discriminant_: an integer logically associated to it +that is used to determine which variant it holds. An opaque reference to this +discriminant can be obtained with the [`mem::discriminant`] function. -These enumerations can be cast to integer types with the `as` operator by a -[numeric cast]. The enumeration can optionally specify which integer each -discriminant gets by following the variant name with `=` followed by a [constant -expression]. If the first variant in the declaration is unspecified, then it is -set to zero. For every other unspecified discriminant, it is set to one higher -than the previous variant in the declaration. +Under the [default representation], the discriminant is interpreted as +an `isize` value. However, the compiler is allowed to use a smaller type (or +another means of distinguishing variants) in its actual memory layout. + +If the [primitive representation] or the [`C` representation] is used, the +leading bytes of a variant (for example, two bytes if `#[repr(u16)]` is used), will +correspond exactly to the discriminant. + +### Assigning Discriminant Values + +#### Explicit Discriminants + +In two circumstances, the discriminant of a variant may be explicitly set by +following the variant name with `=` and a [constant expression]: + +
    +
  1. + +if the enumeration is "C-like" (i.e., it has no tuple or struct variants); e.g.: + +```rust +# #![feature(arbitrary_enum_discriminant)] +enum Enum { + Foo = 3, + Bar = 2, + Baz = 1, +} +``` +
  2. +
  3. + +if a [primitive representation] is used. For example: + +```rust +# #![feature(arbitrary_enum_discriminant)] +#[repr(u8)] +enum Enum { + Unit = 3, + Tuple(u16), + Struct { + a: u8, + b: u16, + } = 1, +} +``` +
  4. +
+ +#### Implicit Discriminants + +If a discriminant for a variant is not specified, then it is set to one higher +than the discriminant of the previous variant in the declaration. If the +discriminant of the first variant in the declaration is unspecified, then +it is set to zero. ```rust enum Foo { @@ -84,14 +130,11 @@ let baz_discriminant = Foo::Baz as u32; assert_eq!(baz_discriminant, 123); ``` -Under the [default representation], the specified discriminant is interpreted as -an `isize` value although the compiler is allowed to use a smaller type in the -actual memory layout. The size and thus acceptable values can be changed by -using a [primitive representation] or the [`C` representation]. +#### Restrictions It is an error when two variants share the same discriminant. -```rust,ignore +```rust,compile_fail enum SharedDiscriminantError { SharedA = 1, SharedB = 1 @@ -107,7 +150,7 @@ enum SharedDiscriminantError2 { It is also an error to have an unspecified discriminant where the previous discriminant is the maximum value for the size of the discriminant. -```rust,ignore +```rust,compile_fail #[repr(u8)] enum OverflowingDiscriminantError { Max = 255, @@ -122,6 +165,53 @@ enum OverflowingDiscriminantError2 { } ``` +### Accessing Discriminant Values + +#### Casting + +If there is no data attached to *any* of the variants of an enumeration, then +the discriminant can be directly accessed with a [numeric cast]; e.g.: + +```rust +enum Enum { + Unit, + Tuple(), + Struct{}, +} + +assert_eq!(0, Enum::Unit as isize); +assert_eq!(1, Enum::Tuple() as isize); +assert_eq!(2, Enum::Struct{} as isize); +``` + +#### Pointer Casting + +If the enumeration specifies a [primitive representation], then the +discriminant may be reliably accessed via unsafe pointer casting: + +```rust +#[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()); +``` + ## Zero-variant Enums Enums with zero variants are known as *zero-variant enums*. As they have diff --git a/src/type-layout.md b/src/type-layout.md index fe611559f..e2cf0af60 100644 --- a/src/type-layout.md +++ b/src/type-layout.md @@ -357,7 +357,7 @@ used with any other representation. [`size_of`]: ../std/mem/fn.size_of.html [`Sized`]: ../std/marker/trait.Sized.html [dynamically sized types]: dynamically-sized-types.html -[C-like enumerations]: items/enumerations.html#custom-discriminant-values-for-field-less-enumerations +[C-like enumerations]: items/enumerations.html#explicit-discriminants [zero-variant enumerations]: items/enumerations.html#zero-variant-enums [undefined behavior]: behavior-considered-undefined.html [27060]: https://github.com/rust-lang/rust/issues/27060