Why does a macro in a type not expand?

What is wrong with my expectations?

macro_rules! dup
{
  ( $T : ty ) =>
  {
    $T, $T,
  };
}

fn main()
{

  let got : ( dup!( i32 ) ) = ( 1, 1 );
  // let got : ( i32, i32, ) = ( 1, 1 );
  let exp : ( i32, i32 ) = ( 1, 1 );
  assert_eq!( got, exp );

}

Error:

error: macro expansion ignores token `,` and any following
  --> src/main.rs:5:7
   |
5  |     $T, $T,
   |       ^
...
12 |   let got : ( dup!( i32 ) ) = ( 1, 1 );
   |               ----------- caused by the macro expansion here
   |
   = note: the usage of `dup!` is likely invalid in type context

warning: unnecessary parentheses around type
  --> src/main.rs:12:13
   |
12 |   let got : ( dup!( i32 ) ) = ( 1, 1 );
   |             ^^^^^^^^^^^^^^^ help: remove these parentheses
   |
   = note: `#[warn(unused_parens)]` on by default

error[E0308]: mismatched types
  --> src/main.rs:12:31
   |
12 |   let got : ( dup!( i32 ) ) = ( 1, 1 );
   |                     ---       ^^^^^^^^ expected `i32`, found tuple
   |                     |
   |                     expected due to this
   |
   = note: expected type `i32`
             found tuple `({integer}, {integer})`

error[E0308]: mismatched types
  --> src/main.rs:15:3
   |
15 |   assert_eq!( got, exp );
   |   ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found tuple
   |
   = note: expected type `i32`
             found tuple `(i32, i32)`
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)

Playground

1 Like

Rust macros are somewhat context sensitive, not purely textual like C macros.

I believe what is going on here is that this:

let got : ( /* to be determined */ ) = ( 1, 1 );
//          ^^^^^^^^^^^^^^^^^^^^^^

is a type context in parenthesis, not a partially-completed-tuple context. Then your macro generates a type ("i32") followed by what the compiler considers garbage (", i32"), because "i32, i32" as a whole isn't a type.

I.e. you need to generate a whole type, not a portion of a type. If your macro generates "(i32, i32,)", it works.

(If there's some larger goal you're trying to accomplish, perhaps you could explain what it is.)

4 Likes

I thought so. Thank you for the clarification @quinedot!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.