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

ide*_*n42 13 introspection rust

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

如果问这个问题是Python,那么答案就是print(dir(object)).
如果问题是C,那么Clang有一个Python API可以解析C代码并对其进行内省.

不熟悉Rust工具,我很想知道是否有某种方法可以在运行时或编译时,使用编译器功能(例如宏)或使用外部工具来执行类似的操作.


请注意,这个问题是故意的,因为使用的确切方法不一定非常重要.给定一个变量,是否有办法找到它的所有方法和功能并不是一个不寻常的目标,但不能很好地了解这个领域,我不是将问题局限于特定方法.

Web*_*rix 7

Is there a way to print out a complete list of available members of a type or instance in Rust?

Currently, there is no such built-in API that you can get the fields at runtime. However you can retrieve fields by using two different ways.

  1. Declarative Macros
  2. Procedural Macros

Solution By Using Declarative Macro

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();
}
Run Code Online (Sandbox Code Playgroud)

This will give you the output:

Field Name: "num" , Field Type: "i32"
Field Name: "s" , Field Type: "String"
Run Code Online (Sandbox Code Playgroud)

Playground


Solution Using Procedural Macro

Since procedural macros are more complicated from the declarative macros, you better to read some references(ref1, ref2, ref3) before starting.

We are going to write a custom derive which is named "Instrospect". To create this custom derive, we need to parse our struct as a TokenStream with the help of syn crate.

#[proc_macro_derive(Introspect)]
pub fn derive_introspect(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemStruct);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

Since our input can be parsed as ItemStruct and ItemStruct has the fields() method in it, we can use this to get fields of our struct.

After we get these fields, we can parse them as named and we can print their field name and field type accordingly.

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

If you want to attach this behavior to your custom derive you can use the following with the help of the quote crate:

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)
Run Code Online (Sandbox Code Playgroud)

Since the behaviour injected to your struct as introspect function, you can call it in your application like following:

#[derive(Introspect)]
struct MyStruct {
    num: i32,
    text: String
}

MyStruct::introspect();
Run Code Online (Sandbox Code Playgroud)

Note: Since the example you are looking for similar to this question. This Proc Macro Answer and Declarative Macro Answer should give you insight as well

  • 这如何解决问题的“主要”部分:*如何自省所有可用的“方法” ***? (2认同)

Aur*_*001 5

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

  • 结构(包括公共成员及其 impl 块)
  • 枚举
  • 性状
  • 职能
  • 板条箱作者用///或编写的任何文档注释//!

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

是输出的示例rustdoc

标准库

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

板条箱

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

你的项目

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

cargo doc
Run Code Online (Sandbox Code Playgroud)

这还将自动为您的依赖项(但不是标准库)生成文档。


Chr*_*son 0

我认为没有任何东西可以开箱即用地做到这一点。

或许可以编写一个编译器插件,通过检查 AST 来实现这一点。

  • 这在 AST 级别上绝非小事。这就是为什么即使像“derive(Clone)”或“println!”这样的内置东西也会产生非常严重的错误。在 HIR 级别(可通过 lint 插件访问),类型和绑定已得到解决,事情会变得容易得多。 (3认同)