From 38516b0641e7b633b08fa2bef051e388e8803c39 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Sun, 14 Jul 2019 16:25:25 -0400 Subject: [PATCH 1/2] Update enum documentation for arbitrary_enum_discriminant feature. --- src/expressions/operator-expr.md | 2 +- src/items/enumerations.md | 132 ++++++++++++++++++++++++++----- src/type-layout.md | 2 +- 3 files changed, 113 insertions(+), 23 deletions(-) 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..5229b37e7 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 (e.g., 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; e.g.: + +```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 From ac9c117c50e2b85a3b388e122c3f2a2e30ccad49 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 15 Jul 2019 13:27:25 -0400 Subject: [PATCH 2/2] "e.g." -> "for example" Co-Authored-By: Ryan Scheel --- src/items/enumerations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/items/enumerations.md b/src/items/enumerations.md index 5229b37e7..f94c06d19 100644 --- a/src/items/enumerations.md +++ b/src/items/enumerations.md @@ -69,7 +69,7 @@ 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 (e.g., two bytes if `#[repr(u16)]` is used), will +leading bytes of a variant (for example, two bytes if `#[repr(u16)]` is used), will correspond exactly to the discriminant. ### Assigning Discriminant Values @@ -95,7 +95,7 @@ enum Enum {
  • -if a [primitive representation] is used; e.g.: +if a [primitive representation] is used. For example: ```rust # #![feature(arbitrary_enum_discriminant)]