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

Rust recursive macro not working for generating struct

我正在尝试编写一个在 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

编写声明性宏 (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 是相当多余的。您可以简单地将 f_list!($fname, $ftype) 替换为 mk_str 中的 pub $fname: $ftype,,它会按书面方式工作。如果这对您的目标不起作用,请查看 The Little Book of Rust Macros. It has some patterns for doing very complicated things with macros. Most of the information in this answer came from the section "Macros in the AST"