Macros 使用宏保持结构字段的可见性

Macros 使用宏保持结构字段的可见性,macros,rust,metaprogramming,encapsulation,Macros,Rust,Metaprogramming,Encapsulation,我正在尝试编写一个Rust宏,它允许我使用结构声明的字段名和类型,但我仍然需要发出结构声明 我已经使用了可选属性和结构的可见性(多亏了),但是我不知道如何处理单个字段中可选存在的pub 到目前为止,我已经: macro_rules! with_generic { ($(#[$struct_meta:meta])* pub struct $name:ident { $($fname:ident : $ftype:ty), *} ) => { with_

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

我已经使用了可选属性和结构的可见性(多亏了),但是我不知道如何处理单个字段中可选存在的
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 MixedPerson {
        pub first_name: String,
        last_name:  String
    }
}
我想得到一些关于如何使宏与最后一个案例一起工作的帮助。我觉得这里可能缺少一些基本的东西,比如用于绑定可见性的类型。如果有一种方法可以在获取字段名和类型的同时绑定整个结构树,那也可以


我还想学习如何让它与具有生存期参数的结构一起工作,但这可能是一个单独的问题。

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

板条箱包含一个宏,该宏显示了完全解析
struct
定义所需的工作。简短版本:您需要单独解析每个字段,其中“has
pub
”和“nothas
pub
”各有一条规则


我还要注意的是,还有一种情况您的宏尚未考虑:字段上的属性,这是字段上的文档注释工作所必需的

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

Rust 1.15是在我提出这个问题后不久推出的,它提供了@DK之类的(自定义派生)支持。他说


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

由于Rust 1.30,您可以将可见性关键字与
vis
说明符匹配。如果没有要匹配的可见性关键字,
vis
元变量将不匹配任何内容,因此您甚至不需要在
$()*
中使用它。这一变化使得
与\u 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
    }
}

“我还要注意的是,还有一种情况你的宏还没有考虑到:字段上的属性,这是它们上的文档注释工作所必需的。”啊,谢谢,我知道我忘了什么。看着你的代码(顺便说一句…惊人的东西),我现在意识到我忘记了很多其他东西,比如where子句。不过你说得对,我基本上想要一个自定义派生:有没有什么地方可以让我读到更多关于宏1.1如何帮助实现这一点的内容?@lloydmeta,这是我所不知道的。我只是想看看源代码,比如
serde-deriver
。谢谢@Shepmaster。我也发现了这个
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
    }
}