我试图编写一个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
}
}
我想获得一些关于如何使宏工作在最后一个案例的帮助。我觉得我可能会遗漏一些基本的东西,例如用于绑定可见性的类型。如果有一种方法可以在获取字段名称和类型的同时绑定整个结构树,那也没关系。
我也想学习如何使用具有生命周期参数的结构,但这可能是一个单独的问题。
答案 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
}
}