Rust递归宏不适用于生成结构

时间:2019-03-06 05:32:05

标签: rust macros rules

我正在尝试编写一个在Rust中生成结构的宏。该宏将根据字段的类型向结构字段添加不同的Serde属性。这是最终目标。

现在,我只是在尝试编写一个宏,该宏使用另一个宏来生成递归代码。

这是代码的样子:

macro_rules! f_list {
    ($fname: ident, $ftype: ty) => {
        pub $fname: $ftype,
    }
}

macro_rules! mk_str {
    ($sname: ident; $($fname: ident: $ftype: ty,)+) => {
        #[derive(Debug, Clone)]
        pub struct $sname {
            $(
                f_list!($fname, $ftype)
            )+
        }
    }
}

mk_str! {
    Yo;
    name: String,
}

fn main() {
    println!("{:?}", Yo { name: "yo".to_string() })
}

此代码在运行时出现以下错误,我无法理解。

error: expected `:`, found `!`
  --> src/main.rs:12:23
   |
12 |                   f_list!($fname, $ftype);
   |                         ^ expected `:`

这是怎么了?

这里是游乐场link

1 个答案:

答案 0 :(得分:1)

编写声明性宏(macro_rules!)时,重要的是要了解宏的输出必须是模式,语句,表达式,项目或impl。实际上,从语法上来说,您应该将输出视为可以独立存在的东西。

在您的代码中,宏f_list(如果可以)将输出类似的代码

name1: type1,
name2: type2,
name3: type3,

虽然这可以是结构声明的一部分,但它本身并不是可以独立存在的东西。

为什么另一个宏中存在错误? mk_str成功扩展到

#[derive(Debug, Clone)]
pub struct Yo {
    f_list!(name, String)
}

但是,解析器不希望在结构声明中使用宏。结构的内部不是模式,语句,表达式,项目或impl。因此,当看到!时,它会放弃并报告错误。

如何解决此问题?在此特定示例中,f_list相当多余。您可以简单地将{{1}中的f_list!($fname, $ftype)替换为pub $fname: $ftype,,它将按书面要求工作。如果这对您的目标不起作用,请查看The Little Book of Rust Macros。它具有一些使用宏来完成非常复杂的事情的模式。此答案中的大多数信息来自"Macros in the AST"部分。