Conditional compilation based on generic constant

Hello,

Does Rust have a way to do something like this?

struct Record<const UNICODE_KEYS : bool> {
    #[if(UNICODE_KEYS)]
    key : String,
    #[if(not(UNICODE_KEYS))]
    key : Box<[u8]>,
}

Unfortunately #[cfg(UNICODE_KEYS)] won't work because the const is part of the struct's generics, and isn't coming from the compiler args / build settings.

Also, I might need to instantiate both kinds of Record within the same program, just as I would using any other generic.

Is this something Rust can support? I know a work-around to create a generic "KeyT" instead of the const bool, but that gets ugly given the different places I need to branch for the different behavior.

Thanks in advance.

In this case you could just always use Box<[u8]> and provide an as_str method that is only available when it is true and that uses std::str::from_utf8_unchecked to do the conversion.

1 Like

That is a very good idea! Thank you! It would solve my problem and probably compile down to the same (or comparably efficient) code.

But I'll read into your silence on the more general point that this isn't generically possible the way C's #define and #if let you do pretty much anything.

Thanks again.

Rust does have conditional compilation like C macros provide, but you can't use const generic values as conditions in conditional compilation.

In general, conditional compilation cannot be used in ways where both combinations actually appear in the program separately.

2 Likes

It's cumbersome (especially because the trait solver does not realize that if const B: bool, then either B == true, or B == false), but it can be done:

trait RecordKeyType { type Key : ?Sized; }

struct __<const UNICODE_KEYS: bool>;

impl RecordKeyType for __<true> {
    type Key = String;
}
impl RecordKeyType for __<false> {
    type Key = Box<[u8]>;
}

struct Record<const UNICODE_KEYS: bool>
where
    __<UNICODE_KEYS> : RecordKeyType,
{
    key: <__<UNICODE_KEYS> as RecordKeyType>::Key,
}

fn _checks (
    yes: Record::<true>,
    no: Record::<false>,
)
{
    let _: String = yes.key;
    let _: Box<[u8]> = no.key;
}

EDIT: Actually, we can use the Record type as the one given as input to the trait, removing the need for the __<…> "const-to-type lifter":

trait ResolveKeyType { type T : ?Sized; }

impl ResolveKeyType for Record<true> {
    type T = String;
}
impl ResolveKeyType for Record<false> {
    type T = Box<[u8]>;
}

struct Record<const UNICODE_KEYS: bool>
where
    Self : ResolveKeyType, // where `UNICODE_KEYS` is `true` or `false`
{
    key: <Self as ResolveKeyType>::T,
}
4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.