Skip to content

feature: const variants of into, from, try_from, and default#147

Open
cosmicexplorer wants to merge 7 commits intoillicitonion:mainfrom
cosmicexplorer:const-into
Open

feature: const variants of into, from, try_from, and default#147
cosmicexplorer wants to merge 7 commits intoillicitonion:mainfrom
cosmicexplorer:const-into

Conversation

@cosmicexplorer
Copy link

@cosmicexplorer cosmicexplorer commented May 3, 2024

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 and effects are able to avoid the need for parts of this, by making From/Into/Default impls const-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 for num_enum derives until Rust has developed a more formal mechanism for manipulating const effects.

Implementation

Four new derive macros were added, along with auxiliary traits/structs in lib.rs as needed. Since traits can't define const fns, 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!

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants