如何反省Rust类型的所有可用方法和成员?

时间:2016-09-01 08:05:28

标签: rust introspection

有没有办法在Rust中打印出类型或实例的可用成员的完整列表?

例如:

  • 在Python中,我可以使用print(dir(object))
  • 在C中,Clang有一个Python API,可以解析C代码并对其进行内省。

由于不熟悉Rust工具,我很想知道是否有某种方法可以在运行时或编译时使用编译器功能(例如宏)或使用外部工具。< / p>

这个问题有意广泛,因为确切的方法并不重要。在任何语言中都希望找到变量的所有方法/函数。我不太了解Rust,我不会将问题局限于特定的发现方法。

我没有定义确切方法的原因是我假设IDE需要这些信息,因此需要有一些内省可用于支持(最终)。据我所知,Rust有类似的东西。

我不认为这是Get fields of a struct type in a macro的副本,因为这个答案可能包括使用外部工具(不一定是宏)。

6 个答案:

答案 0 :(得分:5)

  

是否可以打印出Rust中类型或实例的可用成员的完整列表?

当前,没有这样的内置API可以在运行时获取字段。但是,您可以使用两种不同的方法来检索字段。

  1. Declarative Macros
  2. Procedural Macros

使用声明性宏的解决方案

macro_rules! generate_struct {
    ($name:ident {$($field_name:ident : $field_type:ty),+}) => {
        struct $name { $($field_name: $field_type),+ }
        impl $name {
            fn introspect() {
            $(
            let field_name = stringify!($field_name);
            let field_type = stringify!($field_type);
               println!("Field Name: {:?} , Field Type: {:?}",field_name,field_type);
            )*
            }
        }
    };
}

generate_struct! { MyStruct { num: i32, s: String } }

fn main() {
    MyStruct::introspect();
}

这将为您提供输出:

Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"

Playground


使用程序宏的解决方案

由于程序宏与声明性宏相比更为复杂,因此最好在开始之前阅读一些引用(ref1ref2ref3)。

我们将编写一个名为[custom derive][6]的{​​{1}}。要创建此自定义派生,我们需要借助syn条板箱将结构解析为"Instrospect"

[TokenStream][6]

由于我们的输入可以解析为ItemStruct,并且#[proc_macro_derive(Introspect)] pub fn derive_introspect(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as ItemStruct); // ... } 中具有fields()方法,因此我们可以使用它来获取结构的字段。

获得这些字段后,我们可以将其解析为已命名,并可以相应地打印它们的ItemStructfield name

field type

如果要将这种行为附加到您的自定义派生,则可以在quote板条箱的帮助下使用以下内容:

input
    .fields
    .iter()
    .for_each(|field| match field.parse_named() {
        Ok(field) => println!("{:?}", field),
        Err(_) => println!("Field can not be parsed successfully"),
    });

由于行为是作为自省功能注入到结构中的,因此可以在应用程序中调用它,如下所示:

let name = &input.ident;

let output = quote! {
    impl #name {
        pub fn introspect(){
            input
            .fields
            .iter()
            .for_each(|field| match field.parse_named() {
                Ok(field) => println!("{:?}", field),
                Err(_) => println!("Field can not be parsed successfully"),
             });
        }
    }
};

// Return output TokenStream so your custom derive behavior will be attached.
TokenStream::from(output)

注意:由于示例中您正在寻找与this问题类似的问题。 Proc Macro AnswerDeclarative Macro Answer也应为您提供见识

答案 1 :(得分:2)

要扩展我的评论,您可以使用Rust文档生成器rustdoc来查看您所要求的几乎所有内容(在编译时)。 rustdoc会显示:

  • 结构(包括公共成员及其impl区块)
  • 枚举
  • 性状
  • 功能
  • crate作者使用/////!撰写的任何文档评论。

rustdoc还会自动链接到[src]链接中每个文件的来源。

Hererustdoc的输出示例。

标准库

标准库API参考可用here,可用于std命名空间中的任何内容。

包装箱

您可以在 crates.io 上的docs.rs上获取有关任何包装箱的文档。每次在crates.io上发布时,都会自动为每个包生成文档。

您的项目

您可以使用Cargo为您的项目生成文档,如下所示:

cargo doc

这也会自动生成依赖项的文档(但不是标准库)。

答案 2 :(得分:1)

我认为没有任何东西可以解决这个问题。

可以通过检查AST来编写compiler plugin来做到这一点。

答案 3 :(得分:1)

我有written a very simple crate使用程序宏。它使您可以访问成员信息以及有关您使用的struct/enum的一些简单信息。无法提供有关方法的信息,因为程序宏根本无法获取此信息,据我所知,没有任何方法可以提供此类信息。

答案 4 :(得分:0)

我使用这样的东西:

println!("{:?}", variable); // struct, enum whatever

如果是大型,请使用#版本:

println!("{:#?}", variable); // struct, enum whatever

答案 5 :(得分:0)

如果您需要程序中的字段名称,那么您可能需要使用宏。将结构定义包装在宏和模式匹配中以创建一些函数来获取它们的名称,或者使用过程宏来导出具有这些函数的特征的结构。

请参阅syn中有关派生特征的示例。特别是,请参见包含字段的syn :: Data :: Struct。