是否可以使用传递给宏的项目参数作为方法?
Posted
技术标签:
【中文标题】是否可以使用传递给宏的项目参数作为方法?【英文标题】:Is it possible to use an item arg passed to a macro as a method? 【发布时间】:2017-05-29 22:01:09 【问题描述】:我正在尝试创建一个宏,该宏生成一个struct
,它提供了一组传递给宏的方法。例如,调用:
create_impl!(StructName, fn foo() -> u32 return 432 )
应该生成一个空结构StructName
,它提供方法foo()
。
我最初尝试使用 item
宏 arg 类型。但是,当我尝试在规则中使用 item
时,出现以下编译器错误:
error: expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or ``, found `fn foo() -> u32 return 42; `
--> src/lib.rs:40:13
|
40 | $($function)*
| ^^^^^^^^^
是否可以使用item
参数以这种方式在生成的结构中定义方法?我有什么遗漏吗?
这是我定义的完整宏:
macro_rules! create_impl
($struct_name:ident, $($function:item),*) =>
struct $struct_name
impl $struct_name
// This is the part that fails.
$($function)*
;
【问题讨论】:
我认为 methods 根本不是项目。当我将fn foo()
更改为 fn foo(self)
时,我得到 error: expected one of ::
or :
, found )
(如果您在宏之外编写,则会发生同样的错误) .
【参考方案1】:
简短的回答是“不,您不能将 item
匹配器用于方法”。
根据reference,项目是板条箱或模块中的***事物,因此函数、类型等等。虽然 struct
或 impl
块是一个项目,但它们里面的东西不是。尽管在语法上,方法定义看起来与***函数相同,但这并不能使其成为项目。
Rust 宏系统的工作方式是,一旦片段被解析为 item
,例如使用$foo:item
,它永远是item
;一旦宏展开,它就会被拆分回标记以便重新解析。
这样的结果是$foo:item
只能在宏的输出中的item位置,一般表示***。
有几种选择。
最简单的是使用旧的tt
(令牌树)匹配器。令牌树要么是非括号令牌,要么是由平衡括号包围的令牌序列;所以$(foo:tt)*
匹配任何东西。但是,这意味着它也会吞噬逗号,因此在每个项目周围添加大括号会更容易:
macro_rules! create_impl
($struct_name:ident, $( $($function:tt)* ),*) =>
struct $struct_name
impl $struct_name
$($($function)*)*
;
然后你必须使用额外的大括号:
create_impl!(StructName, fn foo() -> u32 return 432 , fn bar() -> u32 return 765 );
你也可以直接匹配你想要的语法,而不是委托给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)*
当然,因为它是显式的,这意味着如果你想支持没有返回类型的函数,你需要在你的宏中添加另一个 case。
【讨论】:
以上是关于是否可以使用传递给宏的项目参数作为方法?的主要内容,如果未能解决你的问题,请参考以下文章