您可以在没有语法变量的 Rust 宏中使用可选参数吗?

Kev*_*vin 5 macros rust

在 Rust 宏中使用可选参数,$(...)?我希望能够更改结构的制定方式。例如:

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>
}
Run Code Online (Sandbox Code Playgroud)

编写具有重复部分的宏并不是困难的部分,但是,给我带来麻烦的部分是如何使可选的问号更改用作结构体字段类型的类型。到目前为止我已经尝试过这个:

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

宏调用很好并且遵循定义的模式,但是事实证明在标识符Optional之前插入 是很困难的。Field该部分$(Optional)?抛出错误:

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

有没有什么方法可以根据可选宏模式的存在来插入关键字,而不必使用语法变量?

Jmb*_*Jmb 5

你可以通过增量tt咀嚼下推积累来做到这一点:

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)*);
    };
}
Run Code Online (Sandbox Code Playgroud)

操场