Function no_std_compat::mem::discriminant
1.21.0 (const: 1.75.0) · source · pub const fn discriminant<T>(v: &T) -> Discriminant<T>
Expand description
Returns a value uniquely identifying the enum variant in v
.
If T
is not an enum, calling this function will not result in undefined behavior, but the
return value is unspecified.
§Stability
The discriminant of an enum variant may change if the enum definition changes. A discriminant of some variant will not change between compilations with the same compiler. See the Reference for more information.
The value of a Discriminant<T>
is independent of any free lifetimes in T
. As such,
reading or writing a Discriminant<Foo<'a>>
as a Discriminant<Foo<'b>>
(whether via
transmute
or otherwise) is always sound. Note that this is not true for other kinds
of generic parameters and for higher-ranked lifetimes; Discriminant<Foo<A>>
and
Discriminant<Foo<B>>
as well as Discriminant<Bar<dyn for<'a> Trait<'a>>>
and
Discriminant<Bar<dyn Trait<'static>>>
may be incompatible.
§Examples
This can be used to compare enums that carry data, while disregarding the actual data:
use std::mem;
enum Foo { A(&'static str), B(i32), C(i32) }
assert_eq!(mem::discriminant(&Foo::A("bar")), mem::discriminant(&Foo::A("baz")));
assert_eq!(mem::discriminant(&Foo::B(1)), mem::discriminant(&Foo::B(2)));
assert_ne!(mem::discriminant(&Foo::B(3)), mem::discriminant(&Foo::C(3)));
§Accessing the numeric value of the discriminant
Note that it is undefined behavior to transmute
from Discriminant
to a primitive!
If an enum has only unit variants, then the numeric value of the discriminant can be accessed
with an as
cast:
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);
If an enum has opted-in to having a primitive representation for its discriminant, then it’s possible to use pointers to read the memory location storing the discriminant. That cannot be done for enums using the default representation, however, as it’s undefined what layout the discriminant has and where it’s stored — it might not even be stored at all!
#[repr(u8)]
enum Enum {
Unit,
Tuple(bool),
Struct { a: bool },
}
impl Enum {
fn discriminant(&self) -> u8 {
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union`
// between `repr(C)` structs, each of which has the `u8` discriminant as its first
// field, so we can read the discriminant without offsetting the pointer.
unsafe { *<*const _>::from(self).cast::<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());
// ⚠️ This is undefined behavior. Don't do this. ⚠️
// assert_eq!(0, unsafe { std::mem::transmute::<_, u8>(std::mem::discriminant(&unit_like)) });