Hygiene
Macros in Rust are partially hygienic. Specifically, they are hygienic when it comes to most identifiers, but not when it comes to generic type parameters or lifetimes.
Hygiene works by attaching an invisible "syntax context" value to all identifiers. When two identifiers are compared, both the identifiers' textural names and syntax contexts must be identical for the two to be considered equal.
To illustrate this, consider the following code:
macro_rules! using_a {
($e:expr) => {
{
let a = 42;
$e
}
}
}
let four = using_a!(a / 10);
We will use the background colour to denote the syntax context. Now, let's expand the macro invocation:
let four = { let a = 42; a / 10 };
First, recall that macro_rules!
invocations effectively disappear during expansion.
Second, if you attempt to compile this code, the compiler will respond with something along the following lines:
<anon>:11:21: 11:22 error: unresolved name `a`
<anon>:11 let four = using_a!(a / 10);
Note that the background colour (i.e. syntax context) for the expanded macro changes as part of expansion. Each macro expansion is given a new, unique syntax context for its contents. As a result, there are two different a
s in the expanded code: one in the first syntax context, the second in the other. In other words, a
is not the same identifier as a
, however similar they may appear.
That said, tokens that were substituted into the expanded output retain their original syntax context (by virtue of having been provided to the macro as opposed to being part of the macro itself). Thus, the solution is to modify the macro as follows:
macro_rules! using_a {
($a:ident, $e:expr) => {
{
let $a = 42;
$e
}
}
}
let four = using_a!(a, a / 10);
Which, upon expansion becomes:
let four = { let a = 42; a / 10 };
The compiler will accept this code because there is only one a
being used.