是否可以使用传递给宏的项目参数作为方法?
Is it possible to use an item arg passed to a macro as a method?
我正在尝试创建一个生成 struct
的宏,该 struct
提供一组传递到宏中的方法。例如调用:
create_impl!(StructName, fn foo() -> u32 { return 432 })
应该生成一个空结构体 StructName
来提供方法 foo()
.
我最初尝试使用 item
宏参数类型。但是,当我尝试在规则中使用 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)*
}
};
}
简短的回答是 "no, you can't use the item
matcher for a method"。
根据 reference,项目是板条箱或模块中的顶级事物,如功能、类型等。虽然 struct
或 impl
块是一个项目,但其中的东西不是。即使在句法上,方法定义看起来与顶级函数相同,但这并不能使它成为一个项目。
Rust 宏系统的工作方式是,一旦片段被解析为 item
,例如使用 $foo:item
,它就永远是 item
;一旦宏被扩展,它就会被拆分成重新解析的标记。
这样的结果是$foo:item
只能在宏输出的item位置,一般表示top-level.
有几种选择。
最简单的方法是使用旧的 tt
(令牌树)匹配器。令牌树是 non-bracket 令牌或由平衡括号包围的令牌序列;所以 $(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)*
}
}
}
当然,因为它是明确的,这意味着如果你想支持没有 return 类型的函数,你需要在你的宏中添加另一个案例。
我正在尝试创建一个生成 struct
的宏,该 struct
提供一组传递到宏中的方法。例如调用:
create_impl!(StructName, fn foo() -> u32 { return 432 })
应该生成一个空结构体 StructName
来提供方法 foo()
.
我最初尝试使用 item
宏参数类型。但是,当我尝试在规则中使用 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)*
}
};
}
简短的回答是 "no, you can't use the item
matcher for a method"。
根据 reference,项目是板条箱或模块中的顶级事物,如功能、类型等。虽然 struct
或 impl
块是一个项目,但其中的东西不是。即使在句法上,方法定义看起来与顶级函数相同,但这并不能使它成为一个项目。
Rust 宏系统的工作方式是,一旦片段被解析为 item
,例如使用 $foo:item
,它就永远是 item
;一旦宏被扩展,它就会被拆分成重新解析的标记。
这样的结果是$foo:item
只能在宏输出的item位置,一般表示top-level.
有几种选择。
最简单的方法是使用旧的 tt
(令牌树)匹配器。令牌树是 non-bracket 令牌或由平衡括号包围的令牌序列;所以 $(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)*
}
}
}
当然,因为它是明确的,这意味着如果你想支持没有 return 类型的函数,你需要在你的宏中添加另一个案例。