num_enum
num_enum copied to clipboard
feature: const variants of into, from, try_from, and default
Motivation
num_enum
is very effective for manipulating data representations at runtime. However, there are some cases where we might want this functionality in const
contexts, such as when generating static
values or composing num_enum
types into other Copy
structs which we also want to manipulate in const
contexts.
Alternatives
- Note that while I have wanted this feature for both https://github.com/signalapp/libsignal and https://github.com/zip-rs/zip2, I cannot say that it is truly necessary, just convenient.
- Certain nightly features such as
const_trait_impl
andeffects
are able to avoid the need for parts of this, by makingFrom
/Into
/Default
implsconst
-compatible, so for my other work that uses nightly, I actually don't require these at all!- However, I don't believe those features are expected to be stabilized soon, and I think it makes sense to have explicit/separate
const
and non-const
methods fornum_enum
derives until Rust has developed a more formal mechanism for manipulatingconst
effects.
- However, I don't believe those features are expected to be stabilized soon, and I think it makes sense to have explicit/separate
Implementation
Four new derive macros were added, along with auxiliary traits/structs in lib.rs
as needed. Since traits can't define const fn
s, all of these instead generate a method on the enum directly:
-
ConstIntoPrimitive
:const_into(self) -> #repr
-
ConstFromPrimitive
:const_from(#repr) -> Self
-
ConstTryFromPrimitive
:const_try_from(#repr) -> Result<Self, ConstTryFromPrimitiveError<Self>>
-
ConstDefault
:const_default() -> Self
#[derive(num_enum::ConstIntoPrimitive)]
#[repr(u8)]
pub enum E {
Zero = 0,
One = 1,
}
const e: u8 = E::Zero.const_into();
assert_eq!(e, 0);
Additionally, the #[num_enum(method_names(...)]
attribute was added to the parser, to override the names of generated const
methods. This is done because unlike the non-const
derives, we do not have a canonical trait to implement, so we risk stomping on users' method names:
#[derive(num_enum::ConstIntoPrimitive)]
#[num_enum(method_names(const_into = f))]
#[repr(u8)]
pub enum E {
Zero = 0,
One = 1,
}
const e: u8 = E::Zero.f();
assert_eq!(e, 0);
Result
The boilerplate that num_enum
generates no longer has be written by hand when using a num_enum
type in const
contexts!