在Rust

时间:2018-05-13 17:25:21

标签: rust rust-macros

我试图达到这样的目标(简化):

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

fn main() {
    //let x = 50;
    atest!((|| 5 + x));
}

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

error[E0425]: cannot find value `x` in this scope
  --> src/main.rs:10:20
   |
10 |     atest!((|| 5 + x));
   |                    ^ not found in this scope

是否有可能使这项工作?我的理解是在编译之前扩展了宏。

2 个答案:

答案 0 :(得分:5)

  

是否有可能使这项工作?我的理解是在编译之前扩展了宏?

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

这与其他一些语言宏不同,例如C/C++,它们可以在原始文本上运行,如果您不小心,可以让事情变得非常糟糕。

答案 1 :(得分:3)

Peter's answer解释了为什么你所做的事情不会奏效。这是所谓的“宏观卫生”的一部分:宏内部声明的事情可能会泄漏""泄漏"进入周围范围。

您遇到的问题的一个常见解决方法是将标识符的名称作为另一个参数传递给宏:

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))
    };
}

参考