在宏中构建枚举 [英] Building an enum inside a macro

查看:11
本文介绍了在宏中构建枚举的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

宏规则!建造 {($($case:ty),*) =>{ 枚举测试 { $($case),* } };}fn 主要() {构建!{ Foo(i32), Bar(i32, i32) };}

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

注意,如果字段是在枚举里面定义的,是没有问题的:

宏规则!建造 {($($case:ty),*) =>{ 枚举测试 { Foo(i32), Bar(i32, i32) } };}fn 主要() {构建!{ Foo(i32), Bar(i32, i32) };}

如果我的宏只接受简单字段,它也可以工作:

宏规则!建造 {($($case:ident),*) =>{ 枚举测试 { $($case),* } };}fn 主要() {建立!{ Foo, Bar };}

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

解决方案

这绝对有可能,但您将完全不相关的概念混为一谈.

类似于 $case:ty 的东西并不意味着 $case 是一种 看起来 像类型的东西,它意味着 $case 字面意思是一种类型.枚举不是由一系列类型组成的;它们由一系列变体组成,这些变体是一个标识符,后跟(可选)元组结构体、记录结构体或标记值.

解析器并不关心你给它的类型是否碰巧看起来像一个有效的变体,它根本不期待一个类型,并且会拒绝在那个位置解析一个.p>

你需要的是使用像 $case:variant 这样的东西.不幸的是,不存在这样的匹配器.做这样的事情的唯一方法是使用递归增量解析器手动解析它,这 so 超出了 SO 问题的范围,这并不好笑.如果您想了解更多信息,请尝试章节增量TT munchers以 Rust Macros 小书为起点.

但是,您似乎实际上没有对这些案例做任何事情.你只是盲目地替换它们.在这种情况下,您可以作弊,而不必费心尝试匹配任何连贯的内容:

macro_rules!建造 {($($body:tt)*) =>{as_item!{枚举测试 { $($body)* }}};}宏规则!as_item {($i:item) =>{ $i };}fn 主要() {建立!{ Foo, Bar };}

(顺便说一下,as_item! 的内容在 AST 强制(又名重解析技巧").)

这只是获取作为输入提供给 build! 的所有内容,并将其推入 enum 的主体中,而不关心它的外观.

如果你想用变体做一些有意义的事情,那么你将不得不更具体地说明您真正想要完成的工作,因为关于如何进行的最佳建议因答案而异大不相同.

Is it possible to build an enum inside a Rust macro using fields that are defined as macro parameters? I've tried this:

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

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

But it fails with error: expected ident, found 'Foo(i32)'

Note that if the fields are defined inside the enum, there is no problem:

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

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

It also works if my macro only accepts simple fields:

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

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

But I've been unable to get it to work in the general case.

解决方案

It's absolutely possible, but you're conflating totally unrelated concepts.

Something like $case:ty does not mean $case is something which looks like a type, it means $case is literally a type. Enums are not made up of a sequence of types; they're made up of a sequence of variants which are an identifier followed (optionally) by a tuple structure body, a record structure body, or a tag value.

The parser doesn't care if the type you give it happens to coincidentally look like a valid variant, it's simply not expecting a type, and will refuse to parse one in that position.

What you need is to use something like $case:variant. Unfortunately for you, no such matcher exists. The only way to do something like this is to manually parse it using a recursive incremental parser and that is so out of scope of an SO question it's not funny. If you want to learn more, try the chapter on incremental TT munchers in the Little Book of Rust Macros as a starting point.

However, you don't appear to actually do anything with the cases. You're just blindly substituting them. In that case, you can just cheat and not bother with trying to match anything coherent:

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

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

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

(Incidentally, that as_item! thing is explained in the section on AST coercion (a.k.a. "the reparse trick").)

This just grabs everything provided as input to build!, and shoves it into the body of an enum without caring what it looks like.

If you were trying to do something meaningful with the variants, well, you're going to have to be more specific about what you're actually trying to accomplish, as the best advice of how to proceed varies wildly depending on the answer.

这篇关于在宏中构建枚举的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆