safe_discriminant/
lib.rs

1#![no_std]
2//! The [Discriminant] trait, along with its corresponding derive macro,
3//! offers a constant-time and safe method for extracting the primitive
4//! form of the discriminant value for enums.
5//!
6//! The trait is derivable only when the following conditions are met:
7//! 1.  The enum's is accompanied by `#[repr(x)]` attribute,
8//!     where `x` must be one of the supported primitive
9//!     types: `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`,
10//!     or `i64`.
11//!
12//! 2. All enum variants have explicit discriminants.
13//!
14//! 3. Except for `#[repr(x)]`, there are no `#[attr]` style proc-macros
15//!    after `#[derive(Discriminant)]`.
16//! # Usage
17//!
18//! To use this macro, simply annotate your enum with `#[derive(Discriminant)]` as follows:
19//!
20//! ```rust
21//! use safe_discriminant::Discriminant;
22//! #[derive(Discriminant)]
23//! #[repr(u8)]
24//! enum Foo {
25//!     A = 1,
26//!     B(u8) = 2,
27//!     C{inner: u8} = 3,
28//! }
29//! let a = Foo::A;
30//! let b = Foo::B(5);
31//! let c = Foo::C{inner: 6};
32//! assert_eq!(a.discriminant(), 1);
33//! assert_eq!(b.discriminant(), 2);
34//! assert_eq!(c.discriminant(), 3);
35//! ```
36pub use safe_discriminant_derive::Discriminant;
37/// <div class="warning">
38///
39/// # Hazmat
40/// This trait is safe to derive and use but is unsafe to implement manually.
41/// Consider using `#[derive(Discriminant)]` macro instead.
42/// </div>
43///
44/// # Safety
45/// According to
46/// [rust documentation](https://doc.rust-lang.org/std/mem/fn.discriminant.html#accessing-the-numeric-value-of-the-discriminant)
47/// This trait is only safe to implement if the enum has
48/// [primitive representation](https://doc.rust-lang.org/reference/type-layout.html#primitive-representations)
49/// for its discriminant.
50/// That cannot be done for enums using the default representation,
51/// however, as it’s undefined what layout the discriminant has
52/// and where it’s stored — it might not even be stored at all!
53/// The derive macro should take care of checking that it is always safe to call this function.
54
55pub unsafe trait Discriminant {
56    /// The type of *discriminant*, it is represented by the type inside
57    /// `#[repr(u*)]` or `#[repr(i*)]`.
58    type Repr: Copy;
59    /// Returns the discriminant value of enum variant we are using.
60    #[inline(always)]
61    fn discriminant(&self) -> Self::Repr {
62        unsafe { *<*const _>::from(self).cast() }
63    }
64}
65
66#[cfg(test)]
67mod test {
68    #[test]
69    fn test_discriminant() {
70        let t = trybuild::TestCases::new();
71        t.pass("tests/pass/*.rs");
72        t.compile_fail("tests/fail/*.rs")
73    }
74}