Rust: Macro expanded pretty printed code doesn't follow macro hygiene rules

Created on 31 Jan 2019  路  4Comments  路  Source: rust-lang/rust

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.

32922

31856

A-pretty C-bug

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

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

All 4 comments

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);
}
Was this page helpful?
0 / 5 - 0 ratings