Skip to content

Commit cc07ac0

Browse files
authored
Merge pull request #1055 from fee1-dead/arbitrary_enum_discriminant
Update documentation for arbitrary_enum_discriminant feature
2 parents ca43062 + 4f32346 commit cc07ac0

File tree

4 files changed

+160
-23
lines changed

4 files changed

+160
-23
lines changed

src/const_eval.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Conversely, the following are possible in a const function, but not in a const c
113113
[Const parameters]: items/generics.md
114114
[dereference operator]: expressions/operator-expr.md#the-dereference-operator
115115
[destructors]: destructors.md
116-
[enum discriminants]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
116+
[enum discriminants]: items/enumerations.md#discriminants
117117
[expression statements]: statements.md#expression-statements
118118
[expressions]: expressions.md
119119
[field]: expressions/field-expr.md

src/expressions/operator-expr.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ reference types and `mut` or `const` in pointer types.
368368
| Type of `e` | `U` | Cast performed by `e as U` |
369369
|-----------------------|-----------------------|----------------------------------|
370370
| Integer or Float type | Integer or Float type | Numeric cast |
371-
| C-like enum | Integer type | Enum cast |
371+
| Enumeration | Integer type | Enum cast |
372372
| `bool` or `char` | Integer type | Primitive to integer cast |
373373
| `u8` | `char` | `u8` to `char` cast |
374374
| `*T` | `*V` where `V: Sized` \* | Pointer to pointer cast |
@@ -430,6 +430,10 @@ halfway between two floating point numbers.
430430
#### Enum cast
431431

432432
Casts an enum to its discriminant, then uses a numeric cast if needed.
433+
Casting is limited to the following kinds of enumerations:
434+
435+
* [Unit-only enums]
436+
* [Field-less enums] without [explicit discriminants], or where only unit-variants have explicit discriminants
433437

434438
#### Primitive to integer cast
435439

@@ -632,6 +636,8 @@ See [this test] for an example of using this dependency.
632636

633637
[copies or moves]: ../expressions.md#moved-and-copied-types
634638
[dropping]: ../destructors.md
639+
[explicit discriminants]: ../items/enumerations.md#explicit-discriminants
640+
[field-less enums]: ../items/enumerations.md#field-less-enum
635641
[grouped expression]: grouped-expr.md
636642
[literal expression]: literal-expr.md#integer-literal-expressions
637643
[logical and]: ../types/boolean.md#logical-and
@@ -643,6 +649,7 @@ See [this test] for an example of using this dependency.
643649
[assignee expression]: ../expressions.md#place-expressions-and-value-expressions
644650
[undefined behavior]: ../behavior-considered-undefined.md
645651
[unit]: ../types/tuple.md
652+
[Unit-only enums]: ../items/enumerations.md#unit-only-enum
646653
[value expression]: ../expressions.md#place-expressions-and-value-expressions
647654
[temporary value]: ../expressions.md#temporaries
648655
[this test]: https://github.com/rust-lang/rust/blob/1.58.0/src/test/ui/expr/compound-assignment/eval-order.rs

src/items/enumerations.md

+150-20
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
>
1414
> _EnumItem_ :\
1515
> &nbsp;&nbsp; _OuterAttribute_<sup>\*</sup> [_Visibility_]<sup>?</sup>\
16-
> &nbsp;&nbsp; [IDENTIFIER]&nbsp;( _EnumItemTuple_ | _EnumItemStruct_
17-
> | _EnumItemDiscriminant_ )<sup>?</sup>
16+
> &nbsp;&nbsp; [IDENTIFIER]&nbsp;( _EnumItemTuple_ | _EnumItemStruct_ )<sup>?</sup>
17+
> _EnumItemDiscriminant_<sup>?</sup>
1818
>
1919
> _EnumItemTuple_ :\
2020
> &nbsp;&nbsp; `(` [_TupleFields_]<sup>?</sup> `)`
@@ -56,22 +56,71 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
5656
```
5757

5858
In this example, `Cat` is a _struct-like enum variant_, whereas `Dog` is simply
59-
called an enum variant. Each enum instance has a _discriminant_ which is an
60-
integer associated to it that is used to determine which variant it holds. An
61-
opaque reference to this discriminant can be obtained with the
62-
[`mem::discriminant`] function.
59+
called an enum variant.
6360

64-
## Custom Discriminant Values for Fieldless Enumerations
61+
An enum where no constructors contain fields are called a
62+
*<span id="field-less-enum">field-less enum</span>*. For example, this is a fieldless enum:
6563

66-
If there is no data attached to *any* of the variants of an enumeration,
67-
then the discriminant can be directly chosen and accessed.
64+
```rust
65+
enum Fieldless {
66+
Tuple(),
67+
Struct{},
68+
Unit,
69+
}
70+
```
71+
72+
If a field-less enum only contains unit variants, the enum is called an
73+
*<span id="unit-only-enum">unit-only enum</span>*. For example:
74+
75+
```rust
76+
enum Enum {
77+
Foo = 3,
78+
Bar = 2,
79+
Baz = 1,
80+
}
81+
```
82+
83+
<span id="custom-discriminant-values-for-fieldless-enumerations"></span>
84+
## Discriminants
85+
86+
Each enum instance has a _discriminant_: an integer logically associated to it
87+
that is used to determine which variant it holds.
88+
89+
Under the [default representation], the discriminant is interpreted as
90+
an `isize` value. However, the compiler is allowed to use a smaller type (or
91+
another means of distinguishing variants) in its actual memory layout.
92+
93+
### Assigning discriminant values
94+
95+
#### Explicit discriminants
6896

69-
These enumerations can be cast to integer types with the `as` operator by a
70-
[numeric cast]. The enumeration can optionally specify which integer each
71-
discriminant gets by following the variant name with `=` followed by a [constant
72-
expression]. If the first variant in the declaration is unspecified, then it is
73-
set to zero. For every other unspecified discriminant, it is set to one higher
74-
than the previous variant in the declaration.
97+
In two circumstances, the discriminant of a variant may be explicitly set by
98+
following the variant name with `=` and a [constant expression]:
99+
100+
101+
1. if the enumeration is "[unit-only]".
102+
103+
104+
2. if a [primitive representation] is used. For example:
105+
106+
```rust
107+
#[repr(u8)]
108+
enum Enum {
109+
Unit = 3,
110+
Tuple(u16),
111+
Struct {
112+
a: u8,
113+
b: u16,
114+
} = 1,
115+
}
116+
```
117+
118+
#### Implicit discriminants
119+
120+
If a discriminant for a variant is not specified, then it is set to one higher
121+
than the discriminant of the previous variant in the declaration. If the
122+
discriminant of the first variant in the declaration is unspecified, then
123+
it is set to zero.
75124

76125
```rust
77126
enum Foo {
@@ -84,10 +133,7 @@ let baz_discriminant = Foo::Baz as u32;
84133
assert_eq!(baz_discriminant, 123);
85134
```
86135

87-
Under the [default representation], the specified discriminant is interpreted as
88-
an `isize` value although the compiler is allowed to use a smaller type in the
89-
actual memory layout. The size and thus acceptable values can be changed by
90-
using a [primitive representation] or the [`C` representation].
136+
#### Restrictions
91137

92138
It is an error when two variants share the same discriminant.
93139

@@ -122,7 +168,89 @@ enum OverflowingDiscriminantError2 {
122168
}
123169
```
124170

125-
## Zero-variant Enums
171+
### Accessing discriminant
172+
173+
#### Via `mem::discriminant`
174+
175+
[`mem::discriminant`] returns an opaque reference to the discriminant of
176+
an enum value which can be compared. This cannot be used to get the value
177+
of the discriminant.
178+
179+
#### Casting
180+
181+
If an enumeration is [unit-only] (with no tuple and struct variants), then its
182+
discriminant can be directly accessed with a [numeric cast]; e.g.:
183+
184+
```rust
185+
enum Enum {
186+
Foo,
187+
Bar,
188+
Baz,
189+
}
190+
191+
assert_eq!(0, Enum::Foo as isize);
192+
assert_eq!(1, Enum::Bar as isize);
193+
assert_eq!(2, Enum::Baz as isize);
194+
```
195+
196+
[Field-less enums] can be casted if they do not have explicit discriminants, or where only unit variants are explicit.
197+
198+
```rust
199+
enum Fieldless {
200+
Tuple(),
201+
Struct{},
202+
Unit,
203+
}
204+
205+
assert_eq!(0, Fieldless::Tuple() as isize);
206+
assert_eq!(1, Fieldless::Struct{} as isize);
207+
assert_eq!(2, Fieldless::Unit as isize);
208+
209+
#[repr(u8)]
210+
enum FieldlessWithDiscrimants {
211+
First = 10,
212+
Tuple(),
213+
Second = 20,
214+
Struct{},
215+
Unit,
216+
}
217+
218+
assert_eq!(10, FieldlessWithDiscrimants::First as u8);
219+
assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8);
220+
assert_eq!(20, FieldlessWithDiscrimants::Second as u8);
221+
assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8);
222+
assert_eq!(22, FieldlessWithDiscrimants::Unit as u8);
223+
```
224+
225+
#### Pointer casting
226+
227+
If the enumeration specifies a [primitive representation], then the
228+
discriminant may be reliably accessed via unsafe pointer casting:
229+
230+
```rust
231+
#[repr(u8)]
232+
enum Enum {
233+
Unit,
234+
Tuple(bool),
235+
Struct{a: bool},
236+
}
237+
238+
impl Enum {
239+
fn discriminant(&self) -> u8 {
240+
unsafe { *(self as *const Self as *const u8) }
241+
}
242+
}
243+
244+
let unit_like = Enum::Unit;
245+
let tuple_like = Enum::Tuple(true);
246+
let struct_like = Enum::Struct{a: false};
247+
248+
assert_eq!(0, unit_like.discriminant());
249+
assert_eq!(1, tuple_like.discriminant());
250+
assert_eq!(2, struct_like.discriminant());
251+
```
252+
253+
## Zero-variant enums
126254

127255
Enums with zero variants are known as *zero-variant enums*. As they have
128256
no valid values, they cannot be instantiated.
@@ -181,8 +309,10 @@ enum E {
181309
[enumerated type]: ../types/enum.md
182310
[`mem::discriminant`]: ../../std/mem/fn.discriminant.html
183311
[never type]: ../types/never.md
312+
[unit-only]: #unit-only-enum
184313
[numeric cast]: ../expressions/operator-expr.md#semantics
185314
[constant expression]: ../const_eval.md#constant-expressions
186315
[default representation]: ../type-layout.md#the-default-representation
187316
[primitive representation]: ../type-layout.md#primitive-representations
188317
[`C` representation]: ../type-layout.md#the-c-representation
318+
[Field-less enums]: #field-less-enum

src/type-layout.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ used with any other representation.
583583
[`Sized`]: ../std/marker/trait.Sized.html
584584
[`Copy`]: ../std/marker/trait.Copy.html
585585
[dynamically sized types]: dynamically-sized-types.md
586-
[field-less enums]: items/enumerations.md#custom-discriminant-values-for-fieldless-enumerations
586+
[field-less enums]: items/enumerations.md#field-less-enum
587587
[enumerations]: items/enumerations.md
588588
[zero-variant enums]: items/enumerations.md#zero-variant-enums
589589
[undefined behavior]: behavior-considered-undefined.md

0 commit comments

Comments
 (0)