Macro help part 4:

This macro does not compile:

macro_rules! does_not_work {
( $a:tt ; $($b:ident)* ) => {
    $( ($a, $b)),*
};
}

pub fn main() {
    const a: u32 = 1;
    const b: u32 = 2;
    const c: u32 = 3;
    const d: u32 = 4;

    println!(
        "{:?}",
        vec![does_not_work! {
            a ; b c d
        }]
    )
}

it gives me error of:

   |
7  |       $( ($a, $b)),*
   |                   ^
...
19 |           vec![does_not_work! {
   |  ______________-
20 | |             a ; b c d
21 | |         }]
   | |_________- caused by the macro expansion here
   |
   = note: the usage of `does_not_work!` is likely invalid in expression context
help: you might be missing a semicolon here
   |
21 |         };]
   |          +

However, the simple modification of:

macro_rules! this_works {
( $a:tt ; $($b:ident)* ) => {
    $( const _: (u32, u32) = ($a, $b);)*
};
}

const a: u32 = 1;
const b: u32 = 2;
const c: u32 = 3;
const d: u32 = 4;

this_works! {
    a ; b c d
}

does compile.

  1. WTF am I doing wrong ?

  2. Why does the 2nd example even compile ? The $a and $b are bound at different depths, so I'm expecting it to fail.

for one, vec! itself is a macro, and macro expands from outside to inside, so you might not get what you wanted.

but the real reason is that, in your first example, the macro expansion is not valid rust item form, neither does it expand to a statement, nor does it expand to an expression.

rust macro cannot expand to incomplete form like in C/C++. e.g. you cannot achieve something like:

macro_rules! whatever {
    () => {
        A, B, C, D,
    }
}
enum MyEnum {
    whatever!()
}
3 Likes

Knowledgewise, I knew this, but it never hit me until just now. Thanks! This makes sense.

This explains the confusing compiler error. So the output of my macro has to be expression, statement, form (or a sequence of statement / forms ?). No fragments. This makes sense. Thanks.

Specifically, each macro invocation must expand to one of these things:

(I've removed macro_rules itself from this list; as far as I can tell, it was included to show that macro invocations can be included in the expansion of macros, as long as the inner macro invocation is in a valid position after the outer macro is expanded.)

2 Likes

You can generate new macros as well, which is a different can of worms with its own quirks. I'm not sure that's what they mean.