我正在尝试编写一个在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
答案 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"部分。