Macros 可以让宏扩展到结构字段吗?

Macros 可以让宏扩展到结构字段吗?,macros,rust,Macros,Rust,我想执行以下操作,但该位置的宏似乎不起作用(我得到错误:预期为“:”,发现为“!”。如何对单个结构成员进行模式匹配,并根据匹配将属性附加到它们 use serde_derive::Serialize; macro_rules! optional_param { ($name:ident : Option<$type:ty>) => { #[serde(skip_serializing_if = "Option::is_none")] pub $name: Option

我想执行以下操作,但该位置的宏似乎不起作用(我得到
错误:预期为“:”,发现为“!”
。如何对单个结构成员进行模式匹配,并根据匹配将属性附加到它们

use serde_derive::Serialize;

macro_rules! optional_param {
    ($name:ident : Option<$type:ty>) => { #[serde(skip_serializing_if = "Option::is_none")] pub $name: Option<$ty> };
    ($name:ident : Vec   <$type:ty>) => { #[serde(skip_serializing_if = "Vec::is_empty"  )] pub $name: Vec   <$ty> };
    ($name:ident : bool            ) => { #[serde(skip_serializing_if = "bool::not"      )] pub $name: bool        };
}

macro_rules! impl_extra {
    ( $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => (
        #[derive(Default,Debug,Serialize)]
        pub struct $name {
            $( optional_param!($param : $type), )*
        }
    );
}

impl_extra!(MyStruct { member: Option<String> });
使用serde_派生::序列化;
宏规则!可选参数{
($name:ident:Option)=>pub$name:Option};
($name:ident:Vec)=>pub$name:Vec};
($name:ident:bool)=>pub$name:bool};
}
宏规则!impl\u额外{
($name:ident{$($param:ident:$type:ty),*$(,)*})=>(
#[派生(默认、调试、序列化)]
发布结构$name{
$(可选参数!($param:$type),)*
}
);
}
impl_extra!(MyStruct{member:Option});

< p>确实,宏调用在结构定义的中间是无效的。但是,我们可以在其中使用元变量。技巧是,沿着字段定义字段定义,当没有更多的输入要处理时,用一个元定义的字段定义发出一个结构定义。 作为第一步,让我们看看不专门处理字段类型的宏是什么样子的:

macro_rules! impl_extra {
    ( @ $name:ident { } -> ($($result:tt)*) ) => (
        #[derive(Default, Debug, Serialize)]
        pub struct $name {
            $($result)*
        }
    );

    ( @ $name:ident { $param:ident : $type:ty, $($rest:tt)* } -> ($($result:tt)*) ) => (
        impl_extra!(@ $name { $($rest)* } -> (
            $($result)*
            pub $param : $type,
        ));
    );

    ( $name:ident { $( $param:ident : $type:ty ),* $(,)* } ) => (
        impl_extra!(@ $name { $($param : $type,)* } -> ());
    );
}
该宏所做的唯一事情是在每个字段上添加
pub
,并使用
#[派生]
属性定义一个
pub结构。第一个规则处理终端大小写,即当没有更多字段要处理时。第二个规则处理递归大小写,第三个规则处理宏的“public”语法,并将其转换为“处理”语法

请注意,我使用
@
作为内部规则的初始标记,以区分它们与“public”规则。如果不打算将此宏导出到其他板条箱,则您也可以将内部规则移动到其他宏。如果已导出该宏,则可能还必须导出内部规则的单独宏

现在,让我们来处理各种字段类型:

macro_rules! impl_extra {
    ( @ $name:ident { } -> ($($result:tt)*) ) => (
        #[derive(Default, Debug, Serialize)]
        pub struct $name {
            $($result)*
        }
    );

    ( @ $name:ident { $param:ident : Option<$type:ty>, $($rest:tt)* } -> ($($result:tt)*) ) => (
        impl_extra!(@ $name { $($rest)* } -> (
            $($result)*
            #[serde(skip_serializing_if = "Option::is_none")]
            pub $param : Option<$type>,
        ));
    );

    ( @ $name:ident { $param:ident : Vec<$type:ty>, $($rest:tt)* } -> ($($result:tt)*) ) => (
        impl_extra!(@ $name { $($rest)* } -> (
            $($result)*
            #[serde(skip_serializing_if = "Vec::is_empty")]
            pub $param : Vec<$type>,
        ));
    );

    ( @ $name:ident { $param:ident : bool, $($rest:tt)* } -> ($($result:tt)*) ) => (
        impl_extra!(@ $name { $($rest)* } -> (
            $($result)*
            #[serde(skip_serializing_if = "bool::not")]
            pub $param : bool,
        ));
    );

    ( $name:ident { $( $param:ident : $($type:tt)* ),* $(,)* } ) => (
        impl_extra!(@ $name { $($param : $($type)*,)* } -> ());
    );
}
宏规则!impl\u extra{
(@$name:ident{}->($($result:tt)*)=>(
#[派生(默认、调试、序列化)]
发布结构$name{
$($结果)*
}
);
(@$name:ident{$param:ident:Option,$($rest:tt)*}->($($result:tt)*))=>(
impl_extra!(@$name{$($rest)*}->(
$($结果)*
#[serde(跳过序列化(如果=“Option::is\u none”)]
pub$param:Option,
));
);
(@$name:ident{$param:ident:Vec,$($rest:tt)*}->($($result:tt)*))=>(
impl_extra!(@$name{$($rest)*}->(
$($结果)*
#[serde(跳过序列化,如果=“Vec::is_empty”)]
pub$param:Vec,
));
);
(@$name:ident{$param:ident:bool,$($rest:tt)*}->($($result:tt)*))=>(
impl_extra!(@$name{$($rest)*}->(
$($结果)*
#[serde(跳过序列化如果=“bool::not”)]
酒吧$param:bool,
));
);
($name:ident{$($param:ident:$($type:tt)*),*$(,)*})=>(
impl_extra!(@$name{$($param:$($type)*,)*}->());
);
}

请注意,上一条规则有一个不同之处:我们现在不是在
ty
上匹配,而是在
tt
序列上匹配。这是因为一旦宏解析了
ty
,它就不能被分解,所以当我们进行递归宏调用时,
ty
就不可能匹配类似
选项的内容非常感谢!特别是
ty
这句话可能为我节省了一些调试时间!我希望有一种更优雅、更少重复的方式,但显然这只会出现在宏2.0中,afaik将允许宏处于元项位置。