在宏内部构建枚举

时间:2016-05-03 14:22:45

标签: macros rust

是否可以使用定义为宏参数的字段在Rust宏内构建枚举?我试过这个:

macro_rules! build {
    ($($case:ty),*) => { enum Test { $($case),* } };
}

fn main() {
    build!{ Foo(i32), Bar(i32, i32) };
}

但它失败了error: expected ident, found 'Foo(i32)'

请注意,如果字段是在枚举中定义的,则没有问题:

macro_rules! build {
    ($($case:ty),*) => { enum Test { Foo(i32), Bar(i32, i32) } };
}

fn main() {
    build!{ Foo(i32), Bar(i32, i32) };
}

如果我的宏只接受简单字段,它也有效:

macro_rules! build {
    ($($case:ident),*) => { enum Test { $($case),* } };
}

fn main() {
    build!{ Foo, Bar };
}

但是我在一般情况下无法让它工作。

1 个答案:

答案 0 :(得分:8)

这绝对可能,但你把完全不相关的概念混为一谈。

$case:ty之类的内容并不意味着$case看起来类似的内容,这意味着$case 字面上 a类型。枚举不是由一系列类型组成的;它们由一系列变体组成,它们是一个标识符,后跟(可选)由元组结构体,记录结构体或标记值组成。

解析器并不关心你给它的类型是否恰巧看起来就像一个有效的变种,它只是不期望一个类型,并且会拒绝在那个位置解析一个。

您需要使用$case:variant之类的东西。不幸的是,没有这样的匹配器。做这样的事情的唯一方法是使用递归增量解析器手动解析它,并且所以超出SO问题的范围这是不好笑的。如果您想了解更多信息,请尝试chapter on incremental TT munchers in the Little Book of Rust Macros作为起点。

但是,您似乎没有实际对案例做任何事情。你只是盲目地代替他们。在这种情况下,你可以欺骗,而不是试图匹配任何连贯的东西:

macro_rules! build {
    ($($body:tt)*) => {
        as_item! {
            enum Test { $($body)* }
        }
    };
}

macro_rules! as_item {
    ($i:item) => { $i };
}

fn main() {
    build!{ Foo, Bar };
}

(顺便提一下,在AST coercion a.k.a。“重新分析技巧”)一节中解释了as_item!事。)

这只是抓取作为build!的输入提供的所有内容,并将其推入enum的正文中,而不关心其外观。

如果您尝试使用变体做一些有意义的,那么,您将不得不be more specific about what you're actually trying to accomplish,因为关于如何进行的最佳建议会因狂野而变化取决于答案。