Rust 声明式宏和 tt 的作用域

Rust Declarative Macros and Scoping of tt

我在尝试将函数应用于宏内的表达式时遇到了这个问题。我将它浓缩为这个有点愚蠢的最小工作示例,其中我们在花括号内有一个表达式并对其应用一个函数,如下所示:

macro_rules! foo {
    ( {$expression:expr} .apply($($f:tt)+)) => {
        {
            let apply_func = $($f)+;
            apply_func($expression)
        }
    }
}

对于我的用例,我实际上是将该函数应用于多个表达式,但这在这里并不重要。问题是我将局部变量 apply_func 绑定到 apply(...) 内的标记。我很确定本地 let 绑定会做我想做的,我很高兴地说以下表达式都按预期工作:

//WORKS: simple use case
println!("{}", foo!({10}.apply(|x|x*x)));
//WORKS: nested use case
println!("{}", foo!({ foo!({10}.apply(|x|x*x)) }.apply(|x|x+3)));
//WORKS: using some function to pass to the macro
let some_func = |x|x+10;
println!("{}", foo!({ foo!({10}.apply(some_func)) }.apply(|x|x+3)));
//WORKS: calling a function in this scope `apply_func` and using it
//from the inner macro invocation
{
    let apply_func = |x|x*3;
    println!("{}", foo!({ foo!({10}.apply(apply_func)) }.apply(|x|x+3)));
}

结果输出为

100
103
53
33

符合预期。

但是,如果我像这样给出apply_func作为内部宏调用的参数,问题就会出现:

// FAILS: trying to access the apply_func of the outer macro from
// the inner macro without defining it in this scope
println!("{}", foo!({ foo!({10}.apply(apply_func)) }.apply(|x|x+3)));

我担心这会扩展到

{
    let apply_func = |x| x + 3;
    apply_func({
        let apply_func = apply_func;
        apply_func(10)
    })
}

我一点都不喜欢,因为我不希望嵌套范围的 apply_func 实例之间有任何交互。但是在我的机器上(rustc 1.48.0 (7eac88abb 2020-11-16))它给出了一个编译错误

 |     println!("{}", foo!({ foo!({10}.apply(apply_func)) }.apply(|x|x+3)));
 |                                           ^^^^^^^^^^ not found in this scope

太棒了。 Try it on the Rust Playground.

我的问题是:我可以依赖这种行为吗?这真的是预期的吗?有人可以向我解释一下吗?

我知道 Rust 宏不是 C 风格的文本替换。传递给宏的标记是否需要在“粘贴”到宏扩展之前是有效代码?

我会说是的,你可以依赖这种行为。

Rust 宏是卫生的,这意味着编译器会处理由宏创建的标识符,而不是与在宏调用之外创建的标识符发生冲突。

在您的情况下,宏创建的 apply_func 将被视为与您在宏外声明然后提供给宏的标识符完全不同的标识符。

You can read more about it here.