为什么不能将捕获的令牌传递给嵌套宏?

时间:2018-11-23 16:15:02

标签: rust rust-macros

我见过多个例子表明这应该可行,但显然不是:

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

这令人困惑,因为肯定有一条规则要求令牌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)
    }
}

1 个答案:

答案 0 :(得分:3)

some more reading之后,事实证明这是典型绊脚石的一个实例。第一次捕获后,$T会使用 AST节点的值。替换$T不会放置令牌,它将放置AST节点。所以我希望是这样的:

inner_macro!(`f32` [token])

实际上是

inner_macro!(<Type>f32</Type>)

对于用户而言,不幸的是,这两个调用都以相同的方式被字符串化为inner_macro! ( f32 )

正确的方法是将要替换的令牌捕获为“令牌树”:

macro_rules! unknown {
    ($T:tt) => {
        inner_macro!($T)
    }
}