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}