在Rust中的宏中创建闭包环境

在Rust中的宏中创建闭包环境,rust,rust-macros,Rust,Rust Macros,我正在尝试实现这样的目标(简化): 它不起作用,因为在宏求值之前编译器会考虑atest宏的参数: 错误[E0425]:在此范围内找不到值'x' -->src/main.rs:10:20 | 10 |最棒!(| | 5+x)); |^在此范围内找不到 有可能做到这一点吗?我的理解是宏是在编译之前展开的 有可能做到这一点吗?我的理解是宏是在编译之前展开的 宏在编译之前展开,但在解析之前不展开。原始输入代码已经被解析,宏在抽象语法树上操作,而不是在文本上。例如,闭包已经被理解为闭包,其自由变量已经绑

我正在尝试实现这样的目标(简化):

它不起作用,因为在宏求值之前编译器会考虑
atest
宏的参数:

错误[E0425]:在此范围内找不到值'x'
-->src/main.rs:10:20
|
10 |最棒!(| | 5+x));
|^在此范围内找不到
有可能做到这一点吗?我的理解是宏是在编译之前展开的

有可能做到这一点吗?我的理解是宏是在编译之前展开的

宏在编译之前展开,但在解析之前不展开。原始输入代码已经被解析,宏在抽象语法树上操作,而不是在文本上。例如,闭包已经被理解为闭包,其自由变量已经绑定到其词汇范围内的变量

这与其他一些语言的宏不同,例如,宏对原始文本进行操作,如果不小心,它会让你把事情搞得一团糟。

解释了为什么你所做的不起作用。这是所谓“宏卫生”的一部分:宏中声明的内容不能“泄漏”到周围的范围中

针对您面临的问题,一个常见的解决方法是将标识符的名称作为另一个参数传递到宏中:

macro_rules! atest {
    ($x:ident, $closure:tt) => {
        let $x = 5;
        println!("Result is {}", $closure())
    };
}

fn main() {
    atest!(x, (|| 5 + x));
}
这将起作用,因为命名
x
会将其置于调用方的作用域中,即使声明在宏中

您可能会注意到闭包是不必要的,至少在本例中是这样——您可以将
5+x
作为表达式传递给宏,并将其内联展开

macro_rules! atest {
    ($x:ident, $value:expr) => {
        let $x = 5;
        println!("Result is {}", $value)
    };
}
你把这个宏叫做
atest!(x,5+x)
,看起来有点像它自己的闭包。这可能会让你产生编写
atest的想法!(|x | 5+x)
。这也会起作用,变量的作用域是闭包:

macro_rules! atest {
    ($closure:expr) => {
        let x = 5;
        println!("Result is {}", $closure(x))
    };
}
工具书类

谢谢,我知道宏正在AST上工作,但没有意识到自由变量引用是如何处理的。在绑定变量之前,我认为宏片段适合AST中的正确位置。但来自trentcl的解决方案工作得很好——在宏中添加了ident参数,并在正确的位置引入了变量。
macro_rules! atest {
    ($closure:expr) => {
        let x = 5;
        println!("Result is {}", $closure(x))
    };
}