是否可以定义一个宏,从数字生成所有枚举值的静态数组?

时间:2017-03-26 10:35:46

标签: macros rust

我想写这段代码:

enum Foo {
    A1 = 1,
    A17 = 17,
    A42 = 42,
}

static FOO_VALUES: [Foo; 3] = [Foo::A1, Foo::A17, Foo::A42];

impl Foo {
    fn to_num(self) -> i32 {
        match self {
            Foo::A1 => 1,
            //...
        }
        fn from_num(a: i32) -> Result<Foo, ()> {
            //...
        }
    }
}

为了防止错误,我想用宏生成代码:

macro_rules! define_foo_enum {
    ($Name:ident { $($Variant:ident),* }) => {
        pub enum $Name {
            $(concat_ident!(A, $Variant)),*,
        }
        const concat_indent!($Name, _VALUES): &'static [$Name] = &[$($Name::concat_ident!(A, $Variant)),*];
    }
}

宏将像define_foo_enum!(1, 17, 42);

一样使用

这段代码无法编译,我想因为concat_ident!不能按照我尝试使用它的方式工作。另外,我正在为宏提供数字,而不是标识符,但我不知道要使用哪种类型,tt

我正在使用Rust 1.17。

1 个答案:

答案 0 :(得分:3)

我的mashup板条箱可以像您期望的那样形成连接的标识符。自1.15起,它支持任何稳定的Rust编译器,尽管在下面的实现中,我使用了一个关联的常量,该常量要求Rust 1.20+。

#[macro_use]
extern crate mashup;

macro_rules! define_foo_enum {
    ($name:ident { $($number:tt),* }) => {
        mashup! {
            $(
                m["A" $number] = A $number;
            )*
        }
        m! {
            #[derive(Debug, PartialEq)]
            pub enum $name {
                $(
                    "A" $number,
                )*
            }
            impl $name {
                pub const VALUES: &'static [Self] = &[$($name::"A" $number,)*];
            }
        }
    }
}

define_foo_enum!(Foo { 1, 17, 42 });

fn main() {
    assert_eq!(Foo::VALUES, [Foo::A1, Foo::A17, Foo::A42]);
}