| # num_enum |
| |
| Procedural macros to make inter-operation between primitives and enums easier. |
| This crate is no_std compatible. |
| |
| [](https://crates.io/crates/num_enum) |
| [](https://docs.rs/num_enum) |
| [](https://travis-ci.org/illicitonion/num_enum) |
| |
| ## Turning an enum into a primitive |
| |
| ```rust |
| use num_enum::IntoPrimitive; |
| |
| #[derive(IntoPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero, |
| One, |
| } |
| |
| fn main() { |
| let zero: u8 = Number::Zero.into(); |
| assert_eq!(zero, 0u8); |
| } |
| ``` |
| |
| `num_enum`'s `IntoPrimitive` is more type-safe than using `as`, because `as` will silently truncate - `num_enum` only derives `From` for exactly the discriminant type of the enum. |
| |
| ## Attempting to turn a primitive into an enum with try_from |
| |
| ```rust |
| use num_enum::TryFromPrimitive; |
| use std::convert::TryFrom; |
| |
| #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero, |
| One, |
| } |
| |
| fn main() { |
| let zero = Number::try_from(0u8); |
| assert_eq!(zero, Ok(Number::Zero)); |
| |
| let three = Number::try_from(3u8); |
| assert_eq!( |
| three.unwrap_err().to_string(), |
| "No discriminant in enum `Number` matches the value `3`", |
| ); |
| } |
| ``` |
| |
| ### Variant alternatives |
| |
| Sometimes a single enum variant might be representable by multiple numeric values. |
| |
| The `#[num_enum(alternatives = [..])]` attribute allows you to define additional value alternatives for individual variants. |
| |
| (The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) |
| |
| ```rust |
| use num_enum::TryFromPrimitive; |
| use std::convert::TryFrom; |
| |
| #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero = 0, |
| #[num_enum(alternatives = [2])] |
| OneOrTwo = 1, |
| } |
| |
| fn main() { |
| let zero = Number::try_from(0u8); |
| assert_eq!(zero, Ok(Number::Zero)); |
| |
| let one = Number::try_from(1u8); |
| assert_eq!(one, Ok(Number::OneOrTwo)); |
| |
| let two = Number::try_from(2u8); |
| assert_eq!(two, Ok(Number::OneOrTwo)); |
| |
| let three = Number::try_from(3u8); |
| assert_eq!( |
| three.unwrap_err().to_string(), |
| "No discriminant in enum `Number` matches the value `3`", |
| ); |
| } |
| ``` |
| |
| Range expressions are also supported for alternatives, but this requires enabling the `complex-expressions` feature: |
| |
| ```rust |
| use num_enum::TryFromPrimitive; |
| use std::convert::TryFrom; |
| |
| #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero = 0, |
| #[num_enum(alternatives = [2..16])] |
| Some = 1, |
| #[num_enum(alternatives = [17, 18..=255])] |
| Many = 16, |
| } |
| |
| fn main() { |
| let zero = Number::try_from(0u8); |
| assert_eq!(zero, Ok(Number::Zero)); |
| |
| let some = Number::try_from(15u8); |
| assert_eq!(some, Ok(Number::Some)); |
| |
| let many = Number::try_from(255u8); |
| assert_eq!(many, Ok(Number::Many)); |
| } |
| ``` |
| |
| ### Custom error types |
| |
| `TryFromPrimitive` by default will use `num_enum::TryFromPrimitiveError` as its `Error` type. |
| |
| If you want to use a different type, you can use an annotation for this: |
| |
| ```rust |
| use num_enum::TryFromPrimitive; |
| |
| #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] |
| #[num_enum(error_type(name = CustomError, constructor = CustomError::new))] |
| #[repr(u8)] |
| enum FirstNumber { |
| Zero, |
| One, |
| Two, |
| } |
| |
| struct CustomError {} |
| |
| impl CustomError { |
| fn new(value: u8) -> CustomError { |
| CustomError {} |
| } |
| } |
| ``` |
| |
| ## Safely turning a primitive into an exhaustive enum with from_primitive |
| |
| If your enum has all possible primitive values covered, you can derive `FromPrimitive` for it (which auto-implement stdlib's `From`): |
| |
| You can cover all possible values by: |
| * Having variants for every possible value |
| * Having a variant marked `#[num_enum(default)]` |
| * Having a variant marked `#[num_enum(catch_all)]` |
| * Having `#[num_enum(alternatives = [...])`s covering values not covered by a variant. |
| |
| ```rust |
| use num_enum::FromPrimitive; |
| |
| #[derive(Debug, Eq, PartialEq, FromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero, |
| #[num_enum(default)] |
| NonZero, |
| } |
| |
| fn main() { |
| assert_eq!( |
| Number::Zero, |
| Number::from(0_u8), |
| ); |
| assert_eq!( |
| Number::NonZero, |
| Number::from(1_u8), |
| ); |
| } |
| ``` |
| |
| ### Default variant |
| |
| Sometimes it is desirable to have an `Other` variant in an enum that acts as a kind of a wildcard matching all the value not yet covered by other variants. |
| |
| The `#[num_enum(default)]` attribute (or the stdlib `#[default]` attribute) allows you to mark variant as the default. |
| |
| (The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) |
| |
| ```rust |
| use num_enum::FromPrimitive; |
| use std::convert::TryFrom; |
| |
| #[derive(Debug, Eq, PartialEq, FromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero = 0, |
| #[num_enum(default)] |
| NonZero = 1, |
| } |
| |
| fn main() { |
| let zero = Number::from(0u8); |
| assert_eq!(zero, Number::Zero); |
| |
| let one = Number::from(1u8); |
| assert_eq!(one, Number::NonZero); |
| |
| let two = Number::from(2u8); |
| assert_eq!(two, Number::NonZero); |
| } |
| ``` |
| |
| Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them. |
| |
| ### Catch-all variant |
| |
| Sometimes it is desirable to have an `Other` variant which holds the otherwise un-matched value as a field. |
| |
| The `#[num_enum(catch_all)]` attribute allows you to mark at most one variant for this purpose. The variant it's applied to must be a tuple variant with exactly one field matching the `repr` type. |
| |
| ```rust |
| use num_enum::FromPrimitive; |
| use std::convert::TryFrom; |
| |
| #[derive(Debug, Eq, PartialEq, FromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero = 0, |
| #[num_enum(catch_all)] |
| NonZero(u8), |
| } |
| |
| fn main() { |
| let zero = Number::from(0u8); |
| assert_eq!(zero, Number::Zero); |
| |
| let one = Number::from(1u8); |
| assert_eq!(one, Number::NonZero(1_u8)); |
| |
| let two = Number::from(2u8); |
| assert_eq!(two, Number::NonZero(2_u8)); |
| } |
| ``` |
| |
| As this is naturally exhaustive, this is only supported for `FromPrimitive`, not also `TryFromPrimitive`. |
| |
| ## Unsafely turning a primitive into an enum with unchecked_transmute_from |
| |
| If you're really certain a conversion will succeed (and have not made use of `#[num_enum(default)]` or `#[num_enum(alternatives = [..])]` |
| for any of its variants), and want to avoid a small amount of overhead, you can use unsafe code to do this conversion. |
| Unless you have data showing that the match statement generated in the `try_from` above is a bottleneck for you, |
| you should avoid doing this, as the unsafe code has potential to cause serious memory issues in your program. |
| |
| ```rust |
| use num_enum::UnsafeFromPrimitive; |
| |
| #[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero, |
| One, |
| } |
| |
| fn main() { |
| assert_eq!( |
| unsafe { Number::unchecked_transmute_from(0_u8) }, |
| Number::Zero, |
| ); |
| assert_eq!( |
| unsafe { Number::unchecked_transmute_from(1_u8) }, |
| Number::One, |
| ); |
| } |
| |
| unsafe fn undefined_behavior() { |
| let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant! |
| } |
| ``` |
| |
| Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum. |
| If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`. |
| |
| This means, for instance, that the following is undefined behaviour: |
| |
| ```rust,no_run |
| use num_enum::UnsafeFromPrimitive; |
| |
| #[derive(UnsafeFromPrimitive)] |
| #[repr(u8)] |
| enum Number { |
| Zero = 0, |
| |
| // Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]` |
| #[num_enum(default)] |
| One = 1, |
| } |
| let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) }; |
| ``` |
| |
| ## Optional features |
| |
| Some enum values may be composed of complex expressions, for example: |
| |
| ```rust |
| enum Number { |
| Zero = (0, 1).0, |
| One = (0, 1).1, |
| } |
| ``` |
| |
| To cut down on compile time, these are not supported by default, but if you enable the `complex-expressions` |
| feature of your dependency on `num_enum`, these should start working. |
| |
| ## License |
| |
| num_enum may be used under your choice of the BSD 3-clause, Apache 2, or MIT license. |