我正在尝试编写一个在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)
中的pub $fname: $ftype,
替换mk_str
,它将按照书面形式工作。如果这对您的目标不起作用,请查看The Little Book of Rust Macros。它有一些用宏来做非常复杂的事情的模式。这个答案中的大部分信息来自"Macros in the AST"部分。