使用宏保留struct字段可见性

时间:2017-02-02 13:30:03

标签: macros rust metaprogramming encapsulation

我试图编写一个Rust宏,它允许我使用结构声明的字段名称和类型,但我仍然需要发出结构。

我已经使用了可选属性,结构的可见性(感谢The Little Book of Rust Macros),但无法弄清楚如何处理pub的可选存在在各个领域。

到目前为止,我已经:

macro_rules! with_generic {
    ($(#[$struct_meta:meta])*
    pub struct $name:ident { $($fname:ident : $ftype:ty), *}
    ) => {
        with_generic![(pub) $(#[$struct_meta])* struct $name {$($fname: $ftype) ,*}];
    };

    ($(#[$struct_meta:meta])*
    struct $name:ident { $($fname:ident : $ftype:ty), *}
    ) => {
        with_generic![() $(#[$struct_meta])* struct $name {$($fname: $ftype), *}];
    };

    (
    ($($vis:tt)*)
    $(#[$struct_meta:meta])*
    struct $name:ident { $($fname:ident : $ftype:ty), *}
    ) => {
        // emit the struct here
        $(#[$struct_meta])*
        $($vis)* struct $name {
            $($fname: $ftype,)*
        }

        // I work with fname and ftypes here
    }
}

它适用于像

这样的东西
with_generic! {
    #[derive(PartialEq, Eq, Debug)]
    pub struct Person {
        first_name: String,
        last_name:  String
    }
}

with_generic! {
    #[derive(PartialEq, Eq, Debug)]
    struct PrivatePerson {
        first_name: String,
        last_name:  String
    }
}

但不能与

一起使用
with_generic! {
    #[derive(PartialEq, Eq, Debug)]
    struct MixedPerson {
        pub first_name: String,
        last_name:  String
    }
}

我想获得一些关于如何使宏工作在最后一个案例的帮助。我觉得我可能会遗漏一些基本的东西,例如用于绑定可见性的类型。如果有一种方法可以在获取字段名称和类型的同时绑定整个结构树,那也没关系。

我也想学习如何使用具有生命周期参数的结构,但这可能是一个单独的问题。

3 个答案:

答案 0 :(得分:2)

你不能。至少,不是一个单一的非递归规则。这是因为Rust没有可见性的宏匹配器。

parse-macros包含一个parse_struct!宏,显示完全解析struct定义所需的工作。简短版本:您需要单独解析每个字段,每个字段都有一个规则,其中包含pub"并且"没有pub"。

我还要注意,您的宏还没有考虑到另一个案例:字段上的属性,这是文档评论所需的他们工作。

很快,宏1.1应该稳定下来,这可能会提供一种更简单的方法(假设您可以将宏表示为派生,并且不关心旧版本的Rust)。

答案 1 :(得分:1)

在我提出这个问题之后不久,

Rust 1.15被officially released带来了{@ 3}}(自定义派生)支持,就像@DK一样。说。

展望未来,我认为自定义派生w / syn和quote将是执行此类事情的标准方法,并完全支持此问题,因为您不再需要手动重新发出结构。

答案 2 :(得分:0)

自Rust 1.30起,您可以将可见性关键字与vis指定符进行匹配。如果没有匹配的可见性关键字,则vis元变量将不会匹配任何内容,因此您甚至不需要在$()*内部使用它。这一更改使with_generic更加简单:

macro_rules! with_generic {
    ($(#[$struct_meta:meta])*
    $sv:vis struct $name:ident { $($fv:vis $fname:ident : $ftype:ty), *}
    ) => {
        // emit the struct here
        $(#[$struct_meta])*
        $sv struct $name {
            $($fv $fname: $ftype,)*
        }
        // do whatever else you need here
    }
}