是否可以使用传递给宏的项目参数作为方法?

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,项目是板条箱或模块中的***事物,因此函数、类型等等。虽然 structimpl 块是一个项目,但它们里面的东西不是。尽管在语法上,方法定义看起来与***函数相同,但这并不能使其成为项目。

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。

【讨论】:

以上是关于是否可以使用传递给宏的项目参数作为方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ABAP 中的参数调用 excel 宏

Common Lisp:将符号传递给宏

可以将逗号和参数传递给 C++ 宏吗?

将模板类型传递给宏[重复]

是否可以迭代可变参数宏中的参数?

是否可以在 Flutter 的函数中将类作为参数传递?