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

bxfogqkk  于 2022-11-30  发布在  其他
关注(0)|答案(7)|浏览(173)

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

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

由于不熟悉Rust工具,我很想知道是否有某种方法可以做到这一点,无论是在运行时还是编译时,无论是使用编译器特性(例如宏),还是使用外部工具。
这个问题有意地比较宽泛,因为具体的方法并不重要。在任何语言中,想要找到变量的所有方法/函数都是很常见的。由于对Rust不太了解,我没有将这个问题限制在特定的方法上。
我没有定义确切的方法的原因是我假设IDE将需要这些信息,因此将需要一些类型的内省来支持这一点(最终)。
我不认为这是Get fields of a struct type in a macro的重复,因为这个答案可能包括使用外部工具(不一定是宏)。

3qpi33ja

3qpi33ja1#

有没有办法在Rust中打印出类型或示例的可用成员的完整列表?
目前,还没有这样的内置API,你可以在运行时获得字段。但是,你可以通过两种不同的方式检索字段。
1.宣告式宏
1.程序宏

使用宣告式宏的解决方案

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)。
我们将编写一个名为"Instrospect"custom derive。要创建这个自定义派生,我们需要在syn crate的帮助下将我们的结构解析为TokenStream

#[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();
sz81bmfz

sz81bmfz2#

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

  • 结构(包括公共成员及其实现块)
  • 枚举
  • 特质
  • 功能
  • 板条箱作者使用/////!编写的任何文档注解。

rustdoc还自动链接到[src]链接中每个文件的源代码。
Hererustdoc的输出示例。

标准库

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

板条箱

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

您的项目

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

cargo doc

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

gudnpqoy

gudnpqoy3#

我有一个written a very simple crate,它使用了过程宏。它可以让你访问成员信息和一些关于struct/enum的简单信息。关于方法的信息不能给出,因为过程宏根本不能得到这些信息,据我所知,没有任何方法可以给出这些信息。

ctehm74n

ctehm74n4#

我不认为有任何东西可以做到这一点的开箱即用。
可以通过检查AST来编写compiler plugin

f4t66c6m

f4t66c6m5#

如果你在程序中需要字段名,那么你可能需要使用宏。要么用宏和模式匹配来 Package 你的结构体定义,以创建一些函数来获取它们的名称,要么使用过程宏来派生结构体,以获得带有这些函数的特征。
有关派生特性,请参见syn中的示例。特别是,请参见具有字段的syn::Data::Struct。

3phpmpom

3phpmpom6#

根据**@AlexandreMahdhaoui**的问题,我会说:至少在最新的Rust版本中,accepted answer中的proc_macro不起作用,因为您需要使用“#"将令牌传递到quote!中。因此,您可以如下所示尝试smth:

use proc_macro::{TokenStream};
use quote::{quote, ToTokens};
use syn::{parse_macro_input, ItemStruct};

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

    let field_data = input.fields.iter().map(|f| {
        let field_type = f.ty.clone();
        format!(
            "Name={}, Type={}",
            f.ident.clone().unwrap().to_string(),
            quote!(#field_type).to_string()
        )
    }).collect::<Vec<_>>();

    let output = quote! {
        impl #ident {
            pub fn introspect() {
                println!("{:#?}",  vec![#(#field_data),*]);
            }
        }
    };

    TokenStream::from(output)
}

#[derive(Introspect)]
struct Test {
    size: u8,
    test_field: u8,
}

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

关于impl中定义的方法,我没有在输出中找到任何信息,所以不确定是否可能。也许有人可以在评论中分享?

fd3cxomn

fd3cxomn7#

我使用的是这样的方法:

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

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

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

相关问题