我见过的多个例子表明这应该是可能的,但显然不是:
lib.rs
:
#![feature(trace_macros)]
#[macro_export]
macro_rules! inner_macro (
(f32) => {"float"};
);
#[macro_export]
macro_rules! outer_macro {
($T:ty) => {
inner_macro!($T)
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_nested() {
trace_macros!(true);
s1: String = String::from(outer_macro!(f32));
s2: String = String::from(inner_macro!(f32));
trace_macros!(false);
}
}
运行 cargo test
给出以下输出:
error: no rules expected the token `f32`
--> src/lib.rs:11:22
|
4 | / macro_rules! inner_macro (
5 | | (f32) => {"float"};
6 | | );
| |__- when calling this macro
...
11 | inner_macro!($T)
| ^^ no rules expected the token `f32`
...
21 | s1: String = String::from(outer_macro!(f32));
| ----------------- in this macro invocation
这很令人困惑,因为显然有一条规则需要 token f32
。
这两个宏的扩展轨迹也有注释。第一个不起作用:
= note: expanding `outer_macro! { f32 }`
= note: to `inner_macro ! ( f32 )`
= note: expanding `inner_macro! { f32 }`
而第二个是:
= note: expanding `inner_macro! { f32 }`
= note: to `"float"`
为什么 inner_macro!
的第一次扩展失败,而当它没有嵌套在另一个宏中时,完全相同的扩展却成功了?
编辑:如果我们手动执行替换,它会工作并给出预期的输出:
macro_rules! unknown {
($T:ty) => {
inner_macro!(f32)
}
}
最佳答案
在 some more reading 之后,事实证明这是一个典型的绊脚石实例。在第一次被捕获后,$T
获取了一个AST 节点 的值。替换 $T
不会放置 token ,它会放置那个 AST 节点。所以我希望是这样的:
inner_macro!(`f32` [token])
实际上是
inner_macro!(<Type>f32</Type>)
不幸的是,对于用户来说,这两个调用都以相同的方式被字符串化为 inner_macro! ( f32 )
。
执行此操作的正确方法是将要替换的 token 捕获为“ token 树”:
macro_rules! unknown {
($T:tt) => {
inner_macro!($T)
}
}
关于rust - 为什么我不能将捕获的 token 传递给嵌套宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53449947/