是否可以让宏扩展到结构字段?

时间:2018-12-02 12:19:59

标签: macros rust

我想执行以下操作,但是处于该位置的宏似乎不起作用(我得到error: expected `:`, found `!`。如何对单个结构成员进行模式匹配并根据匹配将属性附加到它们?

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> });

Link to the playground

1 个答案:

答案 0 :(得分:4)

实际上,宏调用在结构定义的中间无效。但是,我们可以在那里使用元变量。诀窍是parse the parameters incrementally,一路为字段定义构建标记,然后当没有更多要处理的输入时,发出结构定义,其中字段定义来自一个元变量。

首先,让我们看看不处理字段类型的宏看起来是什么样的:

import mainTemplate from './mainpage.component.html';

此宏唯一要做的就是在每个字段上添加 export class mainPageComponent implements ng.IComponentOptions { public bindings: any; public controller: any; public template: string; public controllerAs: string; constructor() { this.bindings = {}; this.template = mainTemplate, this.controllerAs = "vm"; this.controller =mainPageController; } } 并使用{ test: /\.html$/, exclude: /node_modules/, use: { loader: "html-loader" } } 属性定义一个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 struct

请注意,最后一条规则有所不同:我们现在不匹配#[derive],而是匹配@序列。这是因为一旦宏解析了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)*,)* } -> ()); ); } ,就无法对其进行分解,因此,当我们进行递归宏调用时,ty可能无法与tt之类的东西匹配。