简体   繁体   中英

Can you use an optional argument in a rust macro without syntax variables?

Using an optional argument inside a rust macro $(...)? I'd like to be able to change how a struct is formulated. For example:

struct Field<T>(T);
struct OptionalField<T>(T);

define_struct!(StructName => {
  field_one: i32,
  field_two?: i32
});

// would ideally evaluate to

struct StructName {
  field_one: Field<i32>,
  field_two: OptionalField<i32>
}

Writing a macro with repeated sections isn't the difficult part however, the part that's giving me trouble is how I can make the optional question mark change the type used as the struct's field type. So far I've tried this:

macro_rules! define_struct {
    ($name:ident => {
        $($field_name:ident$(?)?: $type:ty),*
    }) => {
        struct $name {
            $($field_name: $(Optional)?Field<$type>),*
        }
    };
}

The macro invocation is fine and follows the defined pattern however inserting the Optional before the Field identifier is what's proving difficult. The section $(Optional)? throws the error:

attempted to repeat an expression containing no syntax variables matched as repeating at this depth
|
|  $($field_name: $(Optional)?Field<$type>),*
|                  ^^^^^^^^^^

Is there any way to insert a keyword depending upon the presence of an optional macro pattern without having to use a syntax variable?

You can do it with an incremental tt muncher and push-down accumulation :

macro_rules! define_struct {
    (@fields $name:ident { $($fields:tt)* } $field_name:ident?: $type:ty, $($rest:tt)*) => {
        define_struct!(@fields
            $name {
                $($fields)*
                $field_name: OptionalField<$type>,
            }
            $($rest)*);
    };
    (@fields $name:ident { $($fields:tt)* } $field_name:ident: $type:ty, $($rest:tt)*) => {
        define_struct!(@fields
            $name {
                $($fields)*
                $field_name: Field<$type>,
            }
            $($rest)*);
    };
    (@fields $name:ident { $($fields:tt)* }) => {
        struct $name { $($fields)* }
    };
    
    ($name:ident => { $($fields:tt)* }) => {
        define_struct!(@fields $name {} $($fields)*);
    };
}

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM