Search code examples
rustmacros

Accept struct fields in declarative macro in rust?


I want to accept struct members in a declarative macro as a simple way of generating enums with common members (I think I have to do it this way because I want to deserialise from a pre-determined JSON format with serde), but it doesn't work:

What I think should work:

macro_rules! test {
    ($field_1:tt, $field_2:tt) => {
        #[derive(Debug)]
        struct Test {
            first: String,
            $field_1,
            $field_2,
        }
    };
}

test!(a: i32, b: i32);

The error:

   Compiling playground v0.0.1 (/playground)
error: no rules expected the token `:`
  --> src/lib.rs:12:8
   |
1  | macro_rules! test {
   | ----------------- when calling this macro
...
12 | test!(a: i32, b: i32);
   |        ^ no rules expected this token in macro call
   |
note: while trying to match `,`
  --> src/lib.rs:2:17
   |
2  |     ($field_1:tt, $field_2:tt) => {
   |                 ^

error: could not compile `playground` (lib) due to previous error

(rust playground)

Is there any way to do this, or would I have to resort to a proc macro?


Solution

  • A TokenTree (:tt) is a "single token or a delimited sequence of token trees" You're missing delimiters / aren't matching enough of the token trees (there are three per field definition).

    To make this work, you can just match all three token trees that make up the field definition:

    macro_rules! test {
        ($id1:ident : $type1:ty, $id2:ident: $type2:ty) => {
            #[derive(Debug)]
            struct Test {
                first: String,
                $id1: $type1,
                $id2: $type2,
            }
        };
    }
    

    Or for an arbitrary number of field definitions:

    macro_rules! test {
        ($($id:ident : $type:ty),* $(,)?) => {
            #[derive(Debug)]
            struct Test {
                first: String,
                $($id : $type),*
            }
        };
    }