使用宏保持结构字段可见性
Preserving struct field visibility with a macro
我正在尝试编写一个允许我使用字段名称和结构声明类型的 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
}
}
我想获得一些关于如何使宏在最后一个案例中工作的帮助。我觉得我可能在这里遗漏了一些基本的东西,比如用于绑定可见性的类型。如果有办法在获取字段名称和类型的同时绑定整个结构树,那也很好。
我还想了解如何让它与具有生命周期参数的结构一起工作,但也许这应该是一个单独的问题。
你不能。至少,没有一个 non-recursive 规则。这是因为 Rust 没有用于可见性的宏匹配器。
parse-macros
crate contains a parse_struct!
宏显示了完全解析 struct
定义所需的工作。简短版本:您需要单独解析每个字段,每个 "has pub
" 和 "doesn't have pub
".
都有一个规则
我还要注意,还有 另一个 情况,您的宏尚未考虑:字段上的属性,doc-comments 需要这些属性上班。
很快,宏 1.1 应该会稳定下来,这可能会提供一种更简单的方法(假设您可以将宏表示为派生,并且不关心旧版本的 Rust)。
Rust 1.15 是 officially released shortly after I asked this question and brings procedural macros(自定义派生)支持,如 @DK。说。
展望未来,我认为自定义派生 w/syn 和引用将是做这种事情的标准方式,side-steps 这个问题完全解决,因为您不再需要手动 re-emit结构。
从 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
}
}
我正在尝试编写一个允许我使用字段名称和结构声明类型的 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
}
}
我想获得一些关于如何使宏在最后一个案例中工作的帮助。我觉得我可能在这里遗漏了一些基本的东西,比如用于绑定可见性的类型。如果有办法在获取字段名称和类型的同时绑定整个结构树,那也很好。
我还想了解如何让它与具有生命周期参数的结构一起工作,但也许这应该是一个单独的问题。
你不能。至少,没有一个 non-recursive 规则。这是因为 Rust 没有用于可见性的宏匹配器。
parse-macros
crate contains a parse_struct!
宏显示了完全解析 struct
定义所需的工作。简短版本:您需要单独解析每个字段,每个 "has pub
" 和 "doesn't have pub
".
我还要注意,还有 另一个 情况,您的宏尚未考虑:字段上的属性,doc-comments 需要这些属性上班。
很快,宏 1.1 应该会稳定下来,这可能会提供一种更简单的方法(假设您可以将宏表示为派生,并且不关心旧版本的 Rust)。
Rust 1.15 是 officially released shortly after I asked this question and brings procedural macros(自定义派生)支持,如 @DK。说。
展望未来,我认为自定义派生 w/syn 和引用将是做这种事情的标准方式,side-steps 这个问题完全解决,因为您不再需要手动 re-emit结构。
从 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
}
}