我正在尝试创建一个生成 a 的宏,struct该宏提供一组传递到宏中的方法。例如,调用:
create_impl!(StructName, fn foo() -> u32 { return 432 })
Run Code Online (Sandbox Code Playgroud)
应该生成一个StructName提供方法的空结构foo()。
我最初尝试使用item宏 arg 类型。但是,当我尝试item在规则中使用 an 时,出现以下编译器错误:
create_impl!(StructName, fn foo() -> u32 { return 432 })
Run Code Online (Sandbox Code Playgroud)
是否可以使用item参数以这种方式在生成的结构中定义方法?我有什么遗漏的吗?
这是我定义的完整宏:
macro_rules! create_impl {
($struct_name:ident, $($function:item),*) => {
struct $struct_name {
}
impl $struct_name {
// This is the part that fails.
$($function)*
}
};
}
Run Code Online (Sandbox Code Playgroud)
简短的回答是“不,你不能将item匹配器用于方法”。
根据参考资料,项目是板条箱或模块中的顶级事物,因此是函数、类型等。虽然structorimpl块是一个项目,但其中的东西却不是。尽管在语法上,方法定义看起来与顶级函数相同,但这并不意味着它是一个项目。
Rust 宏系统的工作方式是,一旦片段被解析为item,例如 using $foo:item,那么它就永远是item; 。一旦宏展开,它就会被分割回标记以便重新解析。
这样做的结果是$foo:item只能出现在宏的输出中的项目位置,这通常意味着顶级。
有几种选择。
最简单的是使用古老的tt(令牌树)匹配器。令牌树要么是非括号令牌,要么是由平衡括号包围的令牌序列;所以$(foo:tt)*匹配任何东西。然而,这意味着它也会吞噬逗号,因此在每个项目周围添加大括号会更容易:
macro_rules! create_impl {
($struct_name:ident, $({ $($function:tt)* }),*) => {
struct $struct_name {
}
impl $struct_name {
$($($function)*)*
}
};
}
Run Code Online (Sandbox Code Playgroud)
然后你必须将它与额外的大括号一起使用:
create_impl!(StructName, { fn foo() -> u32 { return 432 } }, { fn bar() -> u32 { return 765 } });
Run Code Online (Sandbox Code Playgroud)
您还可以直接匹配您想要的语法,而不是委托给item匹配器:
macro_rules! create_impl2 {
($struct_name:ident, $(fn $fname:ident($($arg:tt)*) -> $t:ty $body:block),*) => {
struct $struct_name {
}
impl $struct_name {
$(fn $fname($($arg)*) -> $t $body)*
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,由于它是显式的,这意味着如果您想支持没有返回类型的函数,则需要向宏中添加另一个情况。