Name |
Date |
Size |
#Lines |
LOC |
||
---|---|---|---|---|---|---|
.. | - | - | ||||
src/ | 25-Apr-2025 | - | 96 | 61 | ||
tests/ | 25-Apr-2025 | - | 1,943 | 1,574 | ||
.cargo-checksum.json | D | 25-Apr-2025 | 10.3 KiB | 1 | 1 | |
Android.bp | D | 25-Apr-2025 | 914 | 36 | 32 | |
Cargo.toml | D | 25-Apr-2025 | 1.6 KiB | 65 | 54 | |
LICENSE | D | 25-Apr-2025 | 9.5 KiB | 177 | 150 | |
LICENSE-APACHE | D | 25-Apr-2025 | 9.5 KiB | 177 | 150 | |
LICENSE-BSD | D | 25-Apr-2025 | 1.4 KiB | 28 | 22 | |
LICENSE-MIT | D | 25-Apr-2025 | 1,023 | 24 | 21 | |
METADATA | D | 25-Apr-2025 | 418 | 18 | 17 | |
MODULE_LICENSE_APACHE2 | D | 25-Apr-2025 | 0 | |||
README.md | D | 25-Apr-2025 | 8 KiB | 317 | 239 | |
cargo_embargo.json | D | 25-Apr-2025 | 43 | 5 | 4 |
README.md
1# num_enum 2 3Procedural macros to make inter-operation between primitives and enums easier. 4This crate is no_std compatible. 5 6[](https://crates.io/crates/num_enum) 7[](https://docs.rs/num_enum) 8[](https://travis-ci.org/illicitonion/num_enum) 9 10## Turning an enum into a primitive 11 12```rust 13use num_enum::IntoPrimitive; 14 15#[derive(IntoPrimitive)] 16#[repr(u8)] 17enum Number { 18 Zero, 19 One, 20} 21 22fn main() { 23 let zero: u8 = Number::Zero.into(); 24 assert_eq!(zero, 0u8); 25} 26``` 27 28`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. 29 30## Attempting to turn a primitive into an enum with try_from 31 32```rust 33use num_enum::TryFromPrimitive; 34use std::convert::TryFrom; 35 36#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 37#[repr(u8)] 38enum Number { 39 Zero, 40 One, 41} 42 43fn main() { 44 let zero = Number::try_from(0u8); 45 assert_eq!(zero, Ok(Number::Zero)); 46 47 let three = Number::try_from(3u8); 48 assert_eq!( 49 three.unwrap_err().to_string(), 50 "No discriminant in enum `Number` matches the value `3`", 51 ); 52} 53``` 54 55### Variant alternatives 56 57Sometimes a single enum variant might be representable by multiple numeric values. 58 59The `#[num_enum(alternatives = [..])]` attribute allows you to define additional value alternatives for individual variants. 60 61(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) 62 63```rust 64use num_enum::TryFromPrimitive; 65use std::convert::TryFrom; 66 67#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 68#[repr(u8)] 69enum Number { 70 Zero = 0, 71 #[num_enum(alternatives = [2])] 72 OneOrTwo = 1, 73} 74 75fn main() { 76 let zero = Number::try_from(0u8); 77 assert_eq!(zero, Ok(Number::Zero)); 78 79 let one = Number::try_from(1u8); 80 assert_eq!(one, Ok(Number::OneOrTwo)); 81 82 let two = Number::try_from(2u8); 83 assert_eq!(two, Ok(Number::OneOrTwo)); 84 85 let three = Number::try_from(3u8); 86 assert_eq!( 87 three.unwrap_err().to_string(), 88 "No discriminant in enum `Number` matches the value `3`", 89 ); 90} 91``` 92 93Range expressions are also supported for alternatives, but this requires enabling the `complex-expressions` feature: 94 95```rust 96use num_enum::TryFromPrimitive; 97use std::convert::TryFrom; 98 99#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 100#[repr(u8)] 101enum Number { 102 Zero = 0, 103 #[num_enum(alternatives = [2..16])] 104 Some = 1, 105 #[num_enum(alternatives = [17, 18..=255])] 106 Many = 16, 107} 108 109fn main() { 110 let zero = Number::try_from(0u8); 111 assert_eq!(zero, Ok(Number::Zero)); 112 113 let some = Number::try_from(15u8); 114 assert_eq!(some, Ok(Number::Some)); 115 116 let many = Number::try_from(255u8); 117 assert_eq!(many, Ok(Number::Many)); 118} 119``` 120 121### Custom error types 122 123`TryFromPrimitive` by default will use `num_enum::TryFromPrimitiveError` as its `Error` type. 124 125If you want to use a different type, you can use an annotation for this: 126 127```rust 128use num_enum::TryFromPrimitive; 129 130#[derive(Debug, Eq, PartialEq, TryFromPrimitive)] 131#[num_enum(error_type(name = CustomError, constructor = CustomError::new))] 132#[repr(u8)] 133enum FirstNumber { 134 Zero, 135 One, 136 Two, 137} 138 139struct CustomError {} 140 141impl CustomError { 142 fn new(value: u8) -> CustomError { 143 CustomError {} 144 } 145} 146``` 147 148## Safely turning a primitive into an exhaustive enum with from_primitive 149 150If your enum has all possible primitive values covered, you can derive `FromPrimitive` for it (which auto-implement stdlib's `From`): 151 152You can cover all possible values by: 153* Having variants for every possible value 154* Having a variant marked `#[num_enum(default)]` 155* Having a variant marked `#[num_enum(catch_all)]` 156* Having `#[num_enum(alternatives = [...])`s covering values not covered by a variant. 157 158```rust 159use num_enum::FromPrimitive; 160 161#[derive(Debug, Eq, PartialEq, FromPrimitive)] 162#[repr(u8)] 163enum Number { 164 Zero, 165 #[num_enum(default)] 166 NonZero, 167} 168 169fn main() { 170 assert_eq!( 171 Number::Zero, 172 Number::from(0_u8), 173 ); 174 assert_eq!( 175 Number::NonZero, 176 Number::from(1_u8), 177 ); 178} 179``` 180 181### Default variant 182 183Sometimes 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. 184 185The `#[num_enum(default)]` attribute (or the stdlib `#[default]` attribute) allows you to mark variant as the default. 186 187(The behavior of `IntoPrimitive` is unaffected by this attribute, it will always return the canonical value.) 188 189```rust 190use num_enum::FromPrimitive; 191use std::convert::TryFrom; 192 193#[derive(Debug, Eq, PartialEq, FromPrimitive)] 194#[repr(u8)] 195enum Number { 196 Zero = 0, 197 #[num_enum(default)] 198 NonZero = 1, 199} 200 201fn main() { 202 let zero = Number::from(0u8); 203 assert_eq!(zero, Number::Zero); 204 205 let one = Number::from(1u8); 206 assert_eq!(one, Number::NonZero); 207 208 let two = Number::from(2u8); 209 assert_eq!(two, Number::NonZero); 210} 211``` 212 213Only `FromPrimitive` pays attention to `default` attributes, `TryFromPrimitive` ignores them. 214 215### Catch-all variant 216 217Sometimes it is desirable to have an `Other` variant which holds the otherwise un-matched value as a field. 218 219The `#[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. 220 221```rust 222use num_enum::FromPrimitive; 223use std::convert::TryFrom; 224 225#[derive(Debug, Eq, PartialEq, FromPrimitive)] 226#[repr(u8)] 227enum Number { 228 Zero = 0, 229 #[num_enum(catch_all)] 230 NonZero(u8), 231} 232 233fn main() { 234 let zero = Number::from(0u8); 235 assert_eq!(zero, Number::Zero); 236 237 let one = Number::from(1u8); 238 assert_eq!(one, Number::NonZero(1_u8)); 239 240 let two = Number::from(2u8); 241 assert_eq!(two, Number::NonZero(2_u8)); 242} 243``` 244 245As this is naturally exhaustive, this is only supported for `FromPrimitive`, not also `TryFromPrimitive`. 246 247## Unsafely turning a primitive into an enum with unchecked_transmute_from 248 249If you're really certain a conversion will succeed (and have not made use of `#[num_enum(default)]` or `#[num_enum(alternatives = [..])]` 250for any of its variants), and want to avoid a small amount of overhead, you can use unsafe code to do this conversion. 251Unless you have data showing that the match statement generated in the `try_from` above is a bottleneck for you, 252you should avoid doing this, as the unsafe code has potential to cause serious memory issues in your program. 253 254```rust 255use num_enum::UnsafeFromPrimitive; 256 257#[derive(Debug, Eq, PartialEq, UnsafeFromPrimitive)] 258#[repr(u8)] 259enum Number { 260 Zero, 261 One, 262} 263 264fn main() { 265 assert_eq!( 266 unsafe { Number::unchecked_transmute_from(0_u8) }, 267 Number::Zero, 268 ); 269 assert_eq!( 270 unsafe { Number::unchecked_transmute_from(1_u8) }, 271 Number::One, 272 ); 273} 274 275unsafe fn undefined_behavior() { 276 let _ = Number::unchecked_transmute_from(2); // 2 is not a valid discriminant! 277} 278``` 279 280Note that this derive ignores any `default`, `catch_all`, and `alternatives` attributes on the enum. 281If you need support for conversions from these values, you should use `TryFromPrimitive` or `FromPrimitive`. 282 283This means, for instance, that the following is undefined behaviour: 284 285```rust,no_run 286use num_enum::UnsafeFromPrimitive; 287 288#[derive(UnsafeFromPrimitive)] 289#[repr(u8)] 290enum Number { 291 Zero = 0, 292 293 // Same for `#[num_enum(catch_all)]`, and `#[num_enum(alternatives = [2, ...])]` 294 #[num_enum(default)] 295 One = 1, 296} 297let _undefined_behavior = unsafe { Number::unchecked_transmute_from(2) }; 298``` 299 300## Optional features 301 302Some enum values may be composed of complex expressions, for example: 303 304```rust 305enum Number { 306 Zero = (0, 1).0, 307 One = (0, 1).1, 308} 309``` 310 311To cut down on compile time, these are not supported by default, but if you enable the `complex-expressions` 312feature of your dependency on `num_enum`, these should start working. 313 314## License 315 316num_enum may be used under your choice of the BSD 3-clause, Apache 2, or MIT license. 317