Skip to content

Commit e06b61c

Browse files
author
Lukas Markeffsky
committed
explain how to get the discriminant out of a #[repr(T)] enum
1 parent 8a75c5a commit e06b61c

File tree

1 file changed

+60
-1
lines changed

1 file changed

+60
-1
lines changed

library/core/src/mem/mod.rs

+60-1
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,10 @@ impl<T> fmt::Debug for Discriminant<T> {
11131113
/// # Stability
11141114
///
11151115
/// The discriminant of an enum variant may change if the enum definition changes. A discriminant
1116-
/// of some variant will not change between compilations with the same compiler.
1116+
/// of some variant will not change between compilations with the same compiler. See the [Reference]
1117+
/// for more information.
1118+
///
1119+
/// [Reference]: ../../reference/items/enumerations.html#discriminants
11171120
///
11181121
/// # Examples
11191122
///
@@ -1129,6 +1132,62 @@ impl<T> fmt::Debug for Discriminant<T> {
11291132
/// assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
11301133
/// assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));
11311134
/// ```
1135+
///
1136+
/// ## Accessing the numeric value of the discriminant
1137+
///
1138+
/// Note that it is *undefined behavior* to [`transmute`] from [`Discriminant`] to a primitive!
1139+
///
1140+
/// If an enum has only unit variants, then the numeric value of the discriminant can be accessed
1141+
/// with an [`as`] cast:
1142+
///
1143+
/// ```
1144+
/// enum Enum {
1145+
/// Foo,
1146+
/// Bar,
1147+
/// Baz,
1148+
/// }
1149+
///
1150+
/// assert_eq!(0, Enum::Foo as isize);
1151+
/// assert_eq!(1, Enum::Bar as isize);
1152+
/// assert_eq!(2, Enum::Baz as isize);
1153+
/// ```
1154+
///
1155+
/// If an enum has opted-in to having a [primitive representation] for its discriminant,
1156+
/// then it's possible to use pointers to read the memory location storing the discriminant.
1157+
/// That **cannot** be done for enums using the [default representation], however, as it's
1158+
/// undefined what layout the discriminant has and where it's stored — it might not even be
1159+
/// stored at all!
1160+
///
1161+
/// [`as`]: ../../std/keyword.as.html
1162+
/// [primitive representation]: ../../reference/type-layout.html#primitive-representations
1163+
/// [default representation]: ../../reference/type-layout.html#the-default-representation
1164+
/// ```
1165+
/// #[repr(u8)]
1166+
/// enum Enum {
1167+
/// Unit,
1168+
/// Tuple(bool),
1169+
/// Struct { a: bool },
1170+
/// }
1171+
///
1172+
/// impl Enum {
1173+
/// fn discriminant(&self) -> u8 {
1174+
/// // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
1175+
/// // between `repr(C)` structs, each of which has the `u8` discriminant as its first
1176+
/// // field, so we can read the discriminant without offsetting the pointer.
1177+
/// unsafe { *<*const _>::from(self).cast::<u8>() }
1178+
/// }
1179+
/// }
1180+
///
1181+
/// let unit_like = Enum::Unit;
1182+
/// let tuple_like = Enum::Tuple(true);
1183+
/// let struct_like = Enum::Struct { a: false };
1184+
/// assert_eq!(0, unit_like.discriminant());
1185+
/// assert_eq!(1, tuple_like.discriminant());
1186+
/// assert_eq!(2, struct_like.discriminant());
1187+
///
1188+
/// // ⚠️ This is undefined behavior. Don't do this. ⚠️
1189+
/// // assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) });
1190+
/// ```
11321191
#[stable(feature = "discriminant_value", since = "1.21.0")]
11331192
#[rustc_const_unstable(feature = "const_discriminant", issue = "69821")]
11341193
#[cfg_attr(not(test), rustc_diagnostic_item = "mem_discriminant")]

0 commit comments

Comments
 (0)