Macro to generate multiple type arguments

I'm trying to generate multiple type arguments using a macro, e.g. I would like Struct<mac!()> to expand to Struct<A, B, ...> but it seems that the compiler has problems understanding commas in the output.

Example code:

macro_rules! mac {
    () => { u8, u16 }
}

struct Struct<X, Y>(X, Y);

fn main() {
    let _: Struct<mac!()> = todo!();
}

gives the following error:

error: macro expansion ignores token `,` and any following
 --> src/main.rs:2:15
  |
2 |     () => { u8, u16 }
  |               ^
...
8 |     let _: Struct<mac!()> = todo!();
  |                   ------ caused by the macro expansion here
  |
  = note: the usage of `mac!` is likely invalid in type context

Is there a way to do this currently?

syntax extension absolutely cannot expand to incomplete or syntactically invalid constructs
Expansion - The Little Book of Rust Macros

So solution can be this:

macro_rules! mac {
    ($id:ident) => { $id<u8, u16> }
}

struct Struct<X, Y>(X, Y);

fn main() {
    let _: mac!(Struct) = todo!(); // expansion: Struct<u8, u16>
}
2 Likes

To get a little more technical, there is a relatively short list of places where a macro invocation can appear. From the reference:

A macro invocation expands a macro at compile time and replaces the invocation with the result of the macro. Macros may be invoked in the following situations:

The macro invocation in Struct<mac!()> is allowed because you can syntactically place a type there, e.g. Struct<i32>. The only way for a macro to represent multiple generic arguments is if they're embedded as part of a type, as @vague demonstrates.

3 Likes