Web*_*rix 20 struct rust rust-macros rust-proc-macros
我有一个自定义结构,如下所示:
struct MyStruct {
first_field: i32,
second_field: String,
third_field: u16,
}
Run Code Online (Sandbox Code Playgroud)
是否有可能以编程方式获取结构字段的数量(例如,通过方法调用field_count()):
let my_struct = MyStruct::new(10, "second_field", 4);
let field_count = my_struct.field_count(); // Expecting to get 3
Run Code Online (Sandbox Code Playgroud)
对于这个结构:
struct MyStruct2 {
first_field: i32,
}
Run Code Online (Sandbox Code Playgroud)
......以下电话应该返回1:
let my_struct_2 = MyStruct2::new(7);
let field_count = my_struct2.field_count(); // Expecting to get count 1
Run Code Online (Sandbox Code Playgroud)
有没有像这样的API field_count()或者只能通过宏获得它?
如果使用宏可以实现这一点,那么应该如何实现?
Luk*_*odt 19
是否有任何可能的API,
field_count()或者只能通过宏获取?
没有这样的内置API可以让您在运行时获取此信息.Rust没有运行时反射(有关更多信息,请参阅此问题).但它确实可以通过proc-macros!
注意:proc-macros与"macro by example"(通过声明macro_rules!)不同.后者不如proc-macros强大.
如果使用宏可以实现这一点,那么应该如何实现?
(这不是对proc-macros的介绍;如果这个主题对你来说是全新的,请先阅读其他地方的介绍.)
在proc-macro(例如自定义派生)中,您需要以某种方式获取结构定义TokenStream.使用TokenStream带有Rust语法的事实上的解决方案是通过syn以下方式解析它:
#[proc_macro_derive(FieldCount)]
pub fn derive_field_count(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
// ...
}
Run Code Online (Sandbox Code Playgroud)
类型input是ItemStruct.如您所见,它具有fields该类型的字段Fields.在该字段上,您可以调用iter()以获取结构的所有字段上的迭代器,您可以在其上调用count():
let field_count = input.fields.iter().count();
Run Code Online (Sandbox Code Playgroud)
现在你有你想要的东西.
也许您想将此field_count()方法添加到您的类型中.你可以通过自定义派生(在quote这里使用箱子)来做到这一点:
let name = &input.ident;
let output = quote! {
impl #name {
pub fn field_count() -> usize {
#field_count
}
}
};
// Return output tokenstream
TokenStream::from(output)
Run Code Online (Sandbox Code Playgroud)
然后,在您的应用程序中,您可以写:
#[derive(FieldCount)]
struct MyStruct {
first_field: i32,
second_field: String,
third_field: u16,
}
MyStruct::field_count(); // returns 3
Run Code Online (Sandbox Code Playgroud)
在这种情况下,你可以仅计算传入宏令牌,如图所示-当由宏产生的结构本身有可能在这里.这就是我想出来的:
macro_rules! gen {
($name:ident {$($field:ident : $t:ty),+}) => {
struct $name { $($field: $t),+ }
impl $name {
fn field_count(&self) -> usize {
gen!(@count $($field),+)
}
}
};
(@count $t1:tt, $($t:tt),+) => { 1 + gen!(@count $($t),+) };
(@count $t:tt) => { 1 };
}
Run Code Online (Sandbox Code Playgroud)
游乐场(有一些测试用例)
这种方法的缺点(一种 - 可能更多)是为这个函数添加一个属性并不简单 - 例如,#[derive(...)]对它的某些东西.另一种方法是编写自定义派生宏,但这是我现在不能谈论的内容.