如何自省所有Rust类型的可用方法和成员? [英] How to introspect all available methods and members of a Rust type?

查看:112
本文介绍了如何自省所有Rust类型的可用方法和成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

例如:

  • 在Python中,我可以使用print(dir(object))
  • 在C语言中,Clang有一个Python API,可以解析C代码并进行内部检查.

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

这个问题是有意提出的,因为确切的方法并不重要.想要找到变量的所有方法/函数在任何语言中都是常见的.不太了解Rust,我并没有将问题局限于特定的发现方法.

我没有定义确切方法的原因是,我认为IDE将需要此信息,因此(最终)需要某种自省功能来支持此信息.就我所知,Rust有类似的东西.

我不认为这是在宏中获取结构类型的字段的副本,因为此答案可以包括使用外部工具(不一定是宏).

解决方案

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

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

  1. 声明性宏
  2. 程序宏


使用声明性宏的解决方案

 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"
 

游乐场


使用程序宏的解决方案

由于程序宏与声明性宏相比更为复杂,因此您最好阅读一些参考文献( ref2 ref3 )之前.

我们将要编写一个名为"Instrospect" custom derive .要创建此自定义派生,我们需要将结构解析为 TokenStream syn 板条箱的帮助.

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

    // ...
}
 

因为我们的输入可以解析为 ItemStruct 并且ItemStruct具有 fields() 方法中,我们可以使用它来获取结构的字段.

获得这些字段后,我们可以将其解析为命名字段,并可以相应地打印它们的field namefield type.

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

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

 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)
 

由于行为是作为自省功能注入到您的结构中的,因此您可以像下面这样在应用程序中调用它:

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

MyStruct::introspect();
 

注意:由于您要查找的示例类似于 Proc宏答案声明性宏答案也应该为您提供见识

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

For example:

  • In Python, I can use print(dir(object))
  • In C, Clang has a Python API that can parse C code and introspect it.

Being unfamiliar with Rust tools, I'm interested to know if there is some way to do this, either at run-time or compile-time, either with compiler features (macros for example), or using external tools.

This question is intentionally broad because the exact method isn't important. It is common in any language to want to find all of a variable's methods/functions. Not knowing Rust well, I'm not limiting the question to specific methods for discovery.

The reason I don't define the exact method is that I assume IDEs will need this information, so there will need to be some kinds of introspection available to support this (eventually). For all I know, Rust has something similar.

I don't think this is a duplicate of Get fields of a struct type in a macro since this answer could include use of external tools (not necessarily macros).

解决方案

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

This will give you the output:

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

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

    // ...
}

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"),
    });

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)

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();

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

这篇关于如何自省所有Rust类型的可用方法和成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆