代码之家  ›  专栏  ›  技术社区  ›  Akiner Alkan

如何以编程方式获取结构的字段数?

  •  19
  • Akiner Alkan  · 技术社区  · 6 年前

    我有如下自定义结构:

    struct MyStruct {
        first_field: i32,
        second_field: String,
        third_field: u16,
    }
    

    是否可以通过编程方式(例如,通过方法调用)获取结构字段的数目 field_count() ):

    let my_struct = MyStruct::new(10, "second_field", 4);
    let field_count = my_struct.field_count(); // Expecting to get 3
    

    对于此结构:

    struct MyStruct2 {
        first_field: i32,
    }
    

    …应返回以下呼叫 1 :

    let my_struct_2 = MyStruct2::new(7);
    let field_count = my_struct2.field_count(); // Expecting to get count 1
    

    有类似API的吗 FieldA计数() 或者只能通过宏来获取?

    如果宏可以实现这一点,那么应该如何实现呢?

    2 回复  |  直到 6 年前
        1
  •  19
  •   hellow Adolfo Casari    6 年前

    有类似API的可能吗 field_count() 或者只能通过宏来获取?

    没有这样的内置API允许您在运行时获取这些信息。生锈没有运行时反射(请参见 this question 更多信息)。但通过proc宏确实是可行的!

    注意:proc宏与“macro by example”(通过 macro_rules! )后者不如proc宏强大。

    如果宏可以实现这一点,那么应该如何实现呢?

    ( 这不是对proc宏的介绍;如果该主题对您来说是全新的,请先在其他地方阅读介绍。 )

    在proc宏(例如自定义派生)中,您可能需要将结构定义作为 TokenStream . 使用 托肯溪 Rust语法是通过 syn :

    #[proc_macro_derive(FieldCount)]
    pub fn derive_field_count(input: TokenStream) -> TokenStream {
        let input = parse_macro_input!(input as ItemStruct);
    
        // ...
    }
    

    类型 input ItemStruct . 正如你所看到的,它有场 fields 类型的 Fields . 在那个领域你可以打电话 iter() 获取结构的所有字段的迭代器,然后可以调用 count() :

    let field_count = input.fields.iter().count();
    

    现在你得到了你想要的。

    也许你想加上这个 FieldA计数() 方法。您可以通过自定义派生(通过使用 quote 板条箱):

    let name = &input.ident;
    
    let output = quote! {
        impl #name {
            pub fn field_count() -> usize {
                #field_count
            }
        }
    };
    
    // Return output tokenstream
    TokenStream::from(output)
    

    然后,在应用程序中,您可以编写:

    #[derive(FieldCount)]
    struct MyStruct {
        first_field: i32,
        second_field: String,
        third_field: u16,
    }
    
    MyStruct::field_count(); // returns 3
    
        2
  •  6
  •   Cerberus    6 年前

    当结构本身由宏生成时是可能的-在这种情况下,您只需计算传递到宏中的令牌,如图所示 here . 我就是这么想的:

    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 };
    }
    

    Playground (有一些测试用例)

    这种方法的缺点(一个——可能还有更多)是,向这个函数添加一个属性并不容易——例如, #[derive(...)] 上面有东西。另一种方法是编写自定义派生宏,但这是目前我不能谈论的事情。