1

Trying to learn Rust macros. I decided to make a macro which operates like the vec! macro, but wraps the arguments in NonZeroUsize. My attempt was:

macro_rules! nzvec {
    ($T:ty, $($x:expr),* ) => {
        vec![$($T::new($x).unwrap()),*];
    }
}

I want calling nzvec![NonZeroU32, 1, 2] to produce vec![NonZeroU32::new(1).unwrap(), NonZeroU32::new(2).unwrap()] and then to further expand using the vec macro. Instead I got an error:

error: no rules expected the token `NonZeroU32`
  --> src/main.rs:5:12
   |
5  |     vec![$($T::new($x).unwrap()),*];
   |            ^^ no rules expected this token in macro call
...
10 |   let vec: Vec<NonZeroU32> = nzvec![NonZeroU32, 1, 2, 3];
   |                              --------------------------- in this macro invocation

I then ran cargo rustc -- -Z trace-macros and got:

note: trace_macro
  --> src/main.rs:10:30
   |
10 |   let vec: Vec<NonZeroU32> = nzvec![NonZeroU32, 1, 2, 3];
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: expanding `nzvec! { NonZeroU32, 1, 2, 3 }`
   = note: to `vec!
           [NonZeroU32 :: new(1).unwrap(), NonZeroU32 :: new(2).unwrap(), NonZeroU32 ::
            new(3).unwrap()] ;`
   = note: expanding `vec! { NonZeroU32 :: new(1).unwrap(), NonZeroU32 :: new(2).unwrap(), NonZeroU32 ::
           new(3).unwrap() }`

which looks to me like the macro is working correctly... what gives?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
Jim
  • 204
  • 2
  • 8

1 Answers1

4

The compiler error could probably be better, but it is because $T::new isn't really valid. It needs to be <$T>::new, then it compiles (playground):

macro_rules! nzvec {
    ($T:ty, $($x:expr),* ) => {
        vec![$(<$T>::new($x).unwrap()),*]
    }
}

See Why do I need angle brackets in <$a> when implementing macro based on type?

kmdreko
  • 42,554
  • 6
  • 57
  • 106