• Home
  • History
  • Annotate
Name
Date
Size
#Lines
LOC

..--

src/25-Apr-2025-9661

tests/25-Apr-2025-1,9431,574

.cargo-checksum.jsonD25-Apr-202510.3 KiB11

Android.bpD25-Apr-2025914 3632

Cargo.tomlD25-Apr-20251.6 KiB6554

LICENSED25-Apr-20259.5 KiB177150

LICENSE-APACHED25-Apr-20259.5 KiB177150

LICENSE-BSDD25-Apr-20251.4 KiB2822

LICENSE-MITD25-Apr-20251,023 2421

METADATAD25-Apr-2025418 1817

MODULE_LICENSE_APACHE2D25-Apr-20250

README.mdD25-Apr-20258 KiB317239

cargo_embargo.jsonD25-Apr-202543 54

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[![crates.io](https://img.shields.io/crates/v/num_enum.svg)](https://crates.io/crates/num_enum)
7[![Documentation](https://docs.rs/num_enum/badge.svg)](https://docs.rs/num_enum)
8[![Build Status](https://travis-ci.org/illicitonion/num_enum.svg?branch=master)](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