我有一个自定义 struct ,如下所示:

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

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

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

对于此 struct :

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

有没有像field_count()这样的API,或者只有通过宏才能实现?

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

推荐答案

有没有像field_count()这样的API,或者只有通过宏才能实现?

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

注:proc宏不同于"示例宏"(通过macro_rules!声明).后者不如proc宏强大.

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

(This is not an introduction into proc-macros; if the topic is completely new to you, first read an introduction elsewhere.)

在proc宏(例如自定义派生)中,您可能需要将 struct 定义设置为TokenStream.使用TokenStream with 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()来获取 struct 的所有字段的迭代器,然后在该字段上调用count():

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

现在你有你想要的了.

也许你想把这个field_count()方法添加到你的类型中.您可以通过自定义派生(使用quote crate ):

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

Rust相关问答推荐

移植带有可变borrow 的C代码-卸载期间错误(nappgui示例)

在rust中如何修改一个盒装函数并将其赋回?

如何使用字符串迭代器执行查找?

通过不同的字段进行散列和排序的 struct (需要不同的EQ实现)

捕获FnMut闭包的时间不够长

使用Py03从Rust调用Python函数时的最佳返回类型

Rust 的多态现象.AsRef与Derf

关于使用平面图功能的borrow 判断器的问题

为什么 GAT、生命周期和异步的这种组合需要 `T: 'static`?

Windows 上 ndarray-linalg 与 mkl-stats 的链接时间错误

如何使用tracing-subscriberRust crate 构建多编写者、全局过滤订阅者

当推送到 HashMap 中的 Vector 时,类型 `()` 无法取消引用

Rust中如何实现一个与Sized相反的负特性(Unsized)

Rust Redis 中的 HSET 命令问题

从现有系列和 map 值创建新系列

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

Rust 中的let是做什么的?

Rust,使用枚举从 HashMap 获取值

将文件的第一行分别读取到文件的其余部分的最有效方法是什么?

如何在宏中的多个参数上编写嵌套循环?