0

I've been looking at rust macros recently and have found conflicting examples of macros using brackets (as laid out below). I'd like to know what the difference between each of these are and which one should be used when building macros. I'd also like to know whether any docs exist for any of this, as I can't find anything on the interwebs.

macro_rules! mac_a {
    ($x:ident,$y:expr) => { // <-- outer curlies
        { // <-- inner curlies
            let $x = $y;
            println!("{} {}", $x, $y);
        }
    };
}

macro_rules! mac_b {
    ($x:ident,$y:expr) => { // <-- outer curlies
        // <-- no inner brackets / curlies
        let $x = $y;
        println!("{} {}", $x, $y);
    };
}

// Does not compile
// macro_rules! mac_c {
//     ($x:ident,$y:expr) => ( // <-- outer brackets
//         ( // <-- inner brackets
//             let $x = $y;
//             println!("{} {}", $x, $y);
//         )
//     );
// }

macro_rules! mac_c2 {
    ($x:expr,$y:expr) => ( // <-- outer brackets
        ( // <-- inner brackets
            println!("{} {}", $x, $y)
        )
    );
}

macro_rules! mac_d {
    ($x:ident,$y:expr) => ( // <-- outer brackets
        // <-- no inner brackets / curlies
        let $x = $y;
        println!("{} {}", $x, $y);
    );
}

fn main() {
    mac_a!(a, 1);
    mac_b!(b, 2);
    // mac_c!(c, 3); // Does not compile
    mac_c2!(3, 3);
    mac_d!(d, 4);
}

All of the above except mac_c compile, and there are differences between each hence the need for mac_c2 with ident and let removed. I don't know why they can't be included ¯\_(ツ)_/¯

goastler
  • 223
  • 2
  • 6

1 Answers1

0

AFAIK the outer curlies/brackets are equivalent and simply serve to delimit each individual macro expansion. OTOH the inner curlies/brackets are part of the generated code and their contents must therefore be legal for where they are used. If we expand the macro invocations in your main functions, we get:

fn main() {
    // mac_a!(a, 1);
    { // <-- inner curlies
        let a = 1;
        println!("{} {}", a, 1);
    }

    // mac_b!(b, 2);
    // <-- no inner brackets / curlies
    let b = 2;
    println!("{} {}", b, 2);

    // mac_c!(c, 3); // Does not compile
    ( // <-- inner brackets
        let c = 3;   // Invalid code
        println!("{} {}", c, 3);
    )

    // mac_c2!(3, 3);
    ( // <-- inner brackets
        println!("{} {}", 3, 3)
    )

    // mac_d!(d, 4);
    // <-- no inner brackets / curlies
    let d = 4;
    println!("{} {}", d, 4);
}

Note BTW that there is therefore a difference for what variables are still around after the macro invocations:

fn main() {
    mac_a!(a, 1);
    mac_b!(b, 2);
    // mac_c!(c, 3); // Does not compile
    mac_c2!(3, 3);
    mac_d!(d, 4);
    
    // println!("{}", a); // Does not compile because the `let a = ...` was done inside curlies
    println!("{} {}", b, d); // Work because `let b = ...` and `let d = ...` were inserted in the current scope
}

Playground

Jmb
  • 18,893
  • 2
  • 28
  • 55
  • How did you expand the macro? – goastler Feb 10 '23 at 14:11
  • 1
    @goastler I expanded them by hand, but you can also expand them by following the playground link and going to `Tools / Expand macros` (this will also expand the `println!` macros, which I didn't do for clarity's sake). – Jmb Feb 10 '23 at 14:34