1

Let's say I have an enum, and I want to somehow annotate or associate each variant with a &str. E.g.:

enum MyEnum {
    A, // "foo"
    B(String), // "bar"
    C(i32) // "baz"
}

I would also like to collect these &str's into an array like

const arr:[&'static str; 3] = ["foo", "bar", "baz"];

Is there a nice way to do this automatically at compile time? Rather than having to write out the constant array separately? Perhaps with macros?

A const fn would answer the first part, but I believe it requires #![feature(const_if_match)], and I would prefer using stable Rust if possible. I'm also not sure it answered the second part. I also considered attribute macros, but I don't know how (or whether) you can collect these attributes together at compile time.

  • Sounds like a job for a macro, but it'll be a custom one. You can annotate each `enum` variant using `#[...]` like you see with `serde`, though I believe you'll need to go down the harder road of macros where you're parsing the syntax in a function and it involves a lot of [`syn`](https://docs.rs/syn/latest/syn/). – tadman Jun 12 '23 at 15:23

1 Answers1

0

You can achieve this with a declarative macro:

macro_rules! str_variants {
    { enum $enum_name:ident {
        $(
        #[str = $variant_str:literal]
        $variant_name:ident $variant_fields:tt
        ),* $(,)*
    } } => {
        enum $enum_name {
            $(
                $variant_name $variant_fields,
            )*
        }

        impl $enum_name {
            const ARR: &[&'static str] = &[
                $(
                    $variant_str,
                )*
            ];
        }
    }
}

str_variants! { enum MyEnum {
    #[str = "foo"]
    A(),
    #[str = "bar"]
    B(String),
    #[str = "baz"]
    C(i32)
} }

fn main() {
    dbg!(MyEnum::ARR);

// [src/main.rs:34] MyEnum::ARR = [
//     "foo",
//     "bar",
//     "baz",
// ]
}

You just need to use an extra pair of parens with unit variants.

playground

PitaJ
  • 12,969
  • 6
  • 36
  • 55