13
13
>
14
14
> _ EnumItem_ :\
15
15
>   ;  ; _ OuterAttribute_ <sup >\* </sup > [ _ Visibility_ ] <sup >?</sup >\
16
- >   ;  ; [ IDENTIFIER]   ; ( _ EnumItemTuple_ | _ EnumItemStruct_
17
- > | _ EnumItemDiscriminant_ ) <sup >?</sup >
16
+ >   ;  ; [ IDENTIFIER]   ; ( _ EnumItemTuple_ | _ EnumItemStruct_ )< sup >?</ sup >
17
+ > _ EnumItemDiscriminant_ <sup >?</sup >
18
18
>
19
19
> _ EnumItemTuple_ :\
20
20
>   ;  ; ` ( ` [ _ TupleFields_ ] <sup >?</sup > ` ) `
@@ -56,22 +56,71 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
56
56
```
57
57
58
58
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.
63
60
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:
65
63
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
68
96
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.
75
124
76
125
``` rust
77
126
enum Foo {
@@ -84,10 +133,7 @@ let baz_discriminant = Foo::Baz as u32;
84
133
assert_eq! (baz_discriminant , 123 );
85
134
```
86
135
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
91
137
92
138
It is an error when two variants share the same discriminant.
93
139
@@ -122,7 +168,89 @@ enum OverflowingDiscriminantError2 {
122
168
}
123
169
```
124
170
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
126
254
127
255
Enums with zero variants are known as * zero-variant enums* . As they have
128
256
no valid values, they cannot be instantiated.
@@ -181,8 +309,10 @@ enum E {
181
309
[ enumerated type ] : ../types/enum.md
182
310
[ `mem::discriminant` ] : ../../std/mem/fn.discriminant.html
183
311
[ never type ] : ../types/never.md
312
+ [ unit-only ] : #unit-only-enum
184
313
[ numeric cast ] : ../expressions/operator-expr.md#semantics
185
314
[ constant expression ] : ../const_eval.md#constant-expressions
186
315
[ default representation ] : ../type-layout.md#the-default-representation
187
316
[ primitive representation ] : ../type-layout.md#primitive-representations
188
317
[ `C` representation ] : ../type-layout.md#the-c-representation
318
+ [ Field-less enums ] : #field-less-enum
0 commit comments