I'm trying to achieve something like this (simplified):
macro_rules! atest {
($closure:tt) => {
let x = 5;
println!("Result is {}", $closure())
};
}
fn main() {
//let x = 50;
atest!((|| 5 + x));
}
It does not work because the argument to the atest
macro is considered by the compiler before macro evaluation:
error[E0425]: cannot find value `x` in this scope
--> src/main.rs:10:20
|
10 | atest!((|| 5 + x));
| ^ not found in this scope
Is it possible to make this work? My understanding was that macros are expanded before compilation.
Macros are expanded before compilation, but not before parsing. The raw input code has already been parsed and macros operate on an abstract syntax tree, not on text. For example, the closure is already understood to be a closure, and its free variables are already bound to variables in its lexical scope.
This is different to some other languages macros, for example C/C++, which operate on raw text, and let you screw things up pretty badly if you're not careful.
Peter's answer explains why what you're doing won't work. This is part of what's referred to as "macro hygiene": things declared inside macros can't "leak" into the surrounding scope.
A common workaround for the problem you're facing is to pass the name of the identifier into the macro as another argument:
This will work because naming
x
puts it in the caller's scope, even though the declaration is inside the macro.You might notice that the closure is kind of unnecessary, at least in this example -- you could just pass
5 + x
as an expression to the macro and have it expanded inline.You call this macro like
atest!(x, 5 + x)
, which looks a little bit like a closure of its own. That might give you the idea of writingatest!(|x| 5 + x)
instead. And that will also work, with a variable scoped to the closure:References