diff --git a/src/const_eval.md b/src/const_eval.md
index c0560376c..34e34d703 100644
--- a/src/const_eval.md
+++ b/src/const_eval.md
@@ -113,7 +113,7 @@ Conversely, the following are possible in a const function, but not in a const c
[Const parameters]: items/generics.md
[dereference operator]: expressions/operator-expr.md#the-dereference-operator
[destructors]: destructors.md
-[enum discriminants]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
+[enum discriminants]: items/enumerations.md#discriminants
[expression statements]: statements.md#expression-statements
[expressions]: expressions.md
[field]: expressions/field-expr.md
diff --git a/src/expressions/operator-expr.md b/src/expressions/operator-expr.md
index d8658d647..9f7e8edac 100644
--- a/src/expressions/operator-expr.md
+++ b/src/expressions/operator-expr.md
@@ -368,7 +368,7 @@ reference types and `mut` or `const` in pointer types.
| 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 |
+| Enumeration | 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 |
@@ -430,6 +430,10 @@ halfway between two floating point numbers.
#### Enum cast
Casts an enum to its discriminant, then uses a numeric cast if needed.
+Casting is limited to the following kinds of enumerations:
+
+* [Unit-only enums]
+* [Field-less enums] without [explicit discriminants], or where only unit-variants have explicit discriminants
#### Primitive to integer cast
@@ -632,6 +636,8 @@ See [this test] for an example of using this dependency.
[copies or moves]: ../expressions.md#moved-and-copied-types
[dropping]: ../destructors.md
+[explicit discriminants]: ../items/enumerations.md#explicit-discriminants
+[field-less enums]: ../items/enumerations.md#field-less-enum
[grouped expression]: grouped-expr.md
[literal expression]: literal-expr.md#integer-literal-expressions
[logical and]: ../types/boolean.md#logical-and
@@ -643,6 +649,7 @@ See [this test] for an example of using this dependency.
[assignee expression]: ../expressions.md#place-expressions-and-value-expressions
[undefined behavior]: ../behavior-considered-undefined.md
[unit]: ../types/tuple.md
+[Unit-only enums]: ../items/enumerations.md#unit-only-enum
[value expression]: ../expressions.md#place-expressions-and-value-expressions
[temporary value]: ../expressions.md#temporaries
[this test]: https://github.com/rust-lang/rust/blob/1.58.0/src/test/ui/expr/compound-assignment/eval-order.rs
diff --git a/src/items/enumerations.md b/src/items/enumerations.md
index 28d3ba873..0d42bfd05 100644
--- a/src/items/enumerations.md
+++ b/src/items/enumerations.md
@@ -13,8 +13,8 @@
>
> _EnumItem_ :\
> _OuterAttribute_\* [_Visibility_]?\
-> [IDENTIFIER] ( _EnumItemTuple_ | _EnumItemStruct_
-> | _EnumItemDiscriminant_ )?
+> [IDENTIFIER] ( _EnumItemTuple_ | _EnumItemStruct_ )?
+> _EnumItemDiscriminant_?
>
> _EnumItemTuple_ :\
> `(` [_TupleFields_]? `)`
@@ -56,22 +56,71 @@ 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 Fieldless Enumerations
+An enum where no constructors contain fields are called a
+*field-less enum*. For example, this is a fieldless enum:
-If there is no data attached to *any* of the variants of an enumeration,
-then the discriminant can be directly chosen and accessed.
+```rust
+enum Fieldless {
+ Tuple(),
+ Struct{},
+ Unit,
+}
+```
+
+If a field-less enum only contains unit variants, the enum is called an
+*unit-only enum*. For example:
+
+```rust
+enum Enum {
+ Foo = 3,
+ Bar = 2,
+ Baz = 1,
+}
+```
+
+
+## Discriminants
+
+Each enum instance has a _discriminant_: an integer logically associated to it
+that is used to determine which variant it holds.
+
+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.
+
+### Assigning discriminant values
+
+#### Explicit discriminants
-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.
+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 "[unit-only]".
+
+
+2. if a [primitive representation] is used. For example:
+
+ ```rust
+ #[repr(u8)]
+ enum Enum {
+ Unit = 3,
+ Tuple(u16),
+ Struct {
+ a: u8,
+ b: u16,
+ } = 1,
+ }
+ ```
+
+#### 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,10 +133,7 @@ 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.
@@ -122,7 +168,89 @@ enum OverflowingDiscriminantError2 {
}
```
-## Zero-variant Enums
+### Accessing discriminant
+
+#### Via `mem::discriminant`
+
+[`mem::discriminant`] returns an opaque reference to the discriminant of
+an enum value which can be compared. This cannot be used to get the value
+of the discriminant.
+
+#### Casting
+
+If an enumeration is [unit-only] (with no tuple and struct variants), then its
+discriminant can be directly accessed with a [numeric cast]; e.g.:
+
+```rust
+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);
+```
+
+[Field-less enums] can be casted if they do not have explicit discriminants, or where only unit variants are explicit.
+
+```rust
+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);
+```
+
+#### 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
no valid values, they cannot be instantiated.
@@ -181,8 +309,10 @@ enum E {
[enumerated type]: ../types/enum.md
[`mem::discriminant`]: ../../std/mem/fn.discriminant.html
[never type]: ../types/never.md
+[unit-only]: #unit-only-enum
[numeric cast]: ../expressions/operator-expr.md#semantics
[constant expression]: ../const_eval.md#constant-expressions
[default representation]: ../type-layout.md#the-default-representation
[primitive representation]: ../type-layout.md#primitive-representations
[`C` representation]: ../type-layout.md#the-c-representation
+[Field-less enums]: #field-less-enum
diff --git a/src/type-layout.md b/src/type-layout.md
index 80a36abb8..191567a42 100644
--- a/src/type-layout.md
+++ b/src/type-layout.md
@@ -583,7 +583,7 @@ used with any other representation.
[`Sized`]: ../std/marker/trait.Sized.html
[`Copy`]: ../std/marker/trait.Copy.html
[dynamically sized types]: dynamically-sized-types.md
-[field-less enums]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
+[field-less enums]: items/enumerations.md#field-less-enum
[enumerations]: items/enumerations.md
[zero-variant enums]: items/enumerations.md#zero-variant-enums
[undefined behavior]: behavior-considered-undefined.md