Creating environment for closure in a macro in Rust

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:

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

fn main() {
    atest!(x, (|| 5 + x));
}

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.

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

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 writing atest!(|x| 5 + x) instead. And that will also work, with a variable scoped to the closure:

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

References

  • Macros in Rust pt1