1313>
1414> _ EnumItem_ :\
1515>   ;  ; _ OuterAttribute_ <sup >\* </sup > [ _ Visibility_ ] <sup >?</sup >\
16- >   ;  ; [ IDENTIFIER]   ; ( _ EnumItemTuple_ | _ EnumItemStruct_
17- > | _ EnumItemDiscriminant_ ) <sup >?</sup >
16+ >   ;  ; [ IDENTIFIER]   ; ( _ EnumItemTuple_ | _ EnumItemStruct_ )< sup >?</ sup >
17+ > _ EnumItemDiscriminant_ <sup >?</sup >
1818>
1919> _ EnumItemTuple_ :\
2020>   ;  ; ` ( ` [ _ TupleFields_ ] <sup >?</sup > ` ) `
@@ -56,22 +56,71 @@ a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 };
5656```
5757
5858In 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
77126enum Foo {
@@ -84,10 +133,7 @@ let baz_discriminant = Foo::Baz as u32;
84133assert_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
92138It 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
127255Enums with zero variants are known as * zero-variant enums* . As they have
128256no 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
0 commit comments