I have a custom struct like the following:
struct MyStruct {
first_field: i32,
second_field: String,
third_field: u16,
}
Is it possible to get the number of struct fields programmatically (like, for example, via a method call field_count()
):
let my_struct = MyStruct::new(10, "second_field", 4);
let field_count = my_struct.field_count(); // Expecting to get 3
For this struct:
struct MyStruct2 {
first_field: i32,
}
... the following call should return 1
:
let my_struct_2 = MyStruct2::new(7);
let field_count = my_struct2.field_count(); // Expecting to get count 1
Is there any API like field_count()
or is it only possible to get that via macros?
If this is achievable with macros, how should it be implemented?
There is no such built-in API that would allow you to get this information at runtime. Rust does not have runtime reflection (see this question for more information). But it is indeed possible via proc-macros!
Note: proc-macros are different from "macro by example" (which is declared via
macro_rules!
). The latter is not as powerful as proc-macros.(This is not an introduction into proc-macros; if the topic is completely new to you, first read an introduction elsewhere.)
In the proc-macro (for example a custom derive), you would somehow need to get the struct definition as
TokenStream
. The de-facto solution to use aTokenStream
with Rust syntax is to parse it viasyn
:The type of
input
isItemStruct
. As you can see, it has the fieldfields
of the typeFields
. On that field you can calliter()
to get an iterator over all fields of the struct, on which in turn you could callcount()
:Now you have what you want.
Maybe you want to add this
field_count()
method to your type. You can do that via the custom derive (by using thequote
crate here):Then, in your application, you can write:
It's possible when the struct itself is generated by the macros - in this case you can just count tokens passed into macros, as shown here. That's what I've come up with:
Playground (with some test cases)
The downside for this approach (one - there could be more) is that it's not trivial to add an attribute to this function - for example, to
#[derive(...)]
something on it. Another approach would be to write the custom derive macros, but this is something that I can't speak about for now.