Hello. I have just started studying rust (and macros), so may not understand fully how it works.
macro_rules! new_bind {
($t:ident, $f:expr) => {
let t = $f;
println!("3. t = {}", t);
};
}
macro_rules! existing_ident {
($t:ident, $f:expr) => {
$t = $f;
println!("1. t = {}", $t);
};
}
fn main() {
let mut t = 0;
existing_ident!(t, 1);
println!("2. t = {}", t);
new_bind!(t, 2);
println!("4. t = {}", t);
}
This code produces following ouptut:
1. t = 1
2. t = 1
3. t = 2
4. t = 1
While I expect following output (also looking at cargo expand results), because new_bind! macro introduces new let binding in the same scope:
1. t = 1
2. t = 1
3. t = 2
4. t = 2
I have found similar issues, but am not sure if they are the same.
This is macro hygiene. It's a feature.
This is actually a bug in cargo expand, not in the macro hygiene. The compiler expands to something closer to:
fn main() {
let mut t = 0;
t = 1;
println!("1. t = {}", t);
println!("2. t = {}", t);
{
let t = 2;
println!("3. t = {}", t);
} // the `t` binding inside this scope goes away after the block goes away and the outer `t` is not accessible due to shadowing
println!("4. t = {}", t); // this `t` is the one from earlier
}
Well, wrapping it in a block gives a variety of wrong impressions about scope, drop order, and how existing_ident! works. I'd say it's much closer to
fn main() {
let mut t = 0;
t = 1;
println!("1. t = {}", t);
println!("2. t = {}", t);
let t_0 = 2;
println!("3. t = {}", t_0);
println!("4. t = {}", t);
}
Closing as dupe of https://github.com/rust-lang/rust/issues/13573.
Most helpful comment
Well, wrapping it in a block gives a variety of wrong impressions about scope, drop order, and how
existing_ident!works. I'd say it's much closer to