[SOLVED] Walk through fields of a Struct

Hello folks
I have a Struct with several Option elements, which are filled by actix-web query.

async fn index(info: web::Query<MQuery>) -> String {...}
struct MQuery {
    lan: Option<String>,
    bsn: Option<String>,
    patid: Option<String>,
    street: Option<String>,
    zip: Option<String>,
    city: Option<String>,

I use Option because I never know which elements will be in the query.

Now I have to convert the content of the Struct into a vector, depending if the content is there or not.

At this moment I have to almost the same code for every element of Struct

if let Some(v) = &self.lastname {
            let s = format!("{:03}3000{}\r\n", v.len() + 9, v);
        if let Some(v) = &self.firstname {
            let s = format!("{:03}3201{}\r\n", v.len() + 9, v);
        if let Some(v) = &self.patid {
            let s = format!("{:03}3202{}\r\n", v.len() + 9, v);

This works but is ugly. Is there a way to "iter" through the Struct so I do not have to repeat the same code so often?
(I know this is not iteration, but "iter" seems to describe best what I want to do)

There is no general way to iterate through a struct; you need some macro to generate code from the struct. Perhaps there is some library that can list out the fields one by one already (I don't know of one if you don't count serde), or you can use macro_rules_attribute to write your own, somewhat messily.

"iter" usually assumes you have a collection of some sort with items of the same type, struct can have fields of any type so no such "iter" exist. You can make code less ugly with a declarative macro.

To make it more generic Rust needs to add support for higher kinded types and a few other goodies. For example in Haskell you'd do something like this: Higher-Kinded Data :: Reasonably Polymorphic - it can cover serialization, validation, etc.

I have fond memories of the way D solved this. Given a structure, you could get a tuple of fields which could then be iterated over with a "static foreach" that unrolled at compile-time. [1] Made for some very compact and very powerful serialisation libraries.

  1. Of course, it also had absolutely unholy tuples where the elements were simultaneously names, references, types, and extra metadata that didn't have any in-language representation (but could be extracted by converting them to strings and then re-parsing them using CTFE), leading to some completely bonkers metaprogramming code. ↩ī¸Ž

you can pass the struct definition to a custom macro to generate the boilerplate code for you, but there's no general mechanism to inspect the fields at a type level after the struct's definition is already checked by the compiler.

you can define a macro to reduce the verbosity I guess. something like:

1 Like

Yea, GHC generics in Haskell. Given any datatype you get its generic representation in terms of about 7 primitive types that give you access to all the information about it. I made a serialization library myself using this approach :slight_smile:

An alternative to a macro is to simply factor out the common code.

        fn make_list(&self) -> Vec<Vec<u8>> {
            let mut l = Vec::new();
            let mut add_to_list = |opt_v: &Option<String>, num| {
                if let Some(v) = opt_v {
                    let s = format!("{:03}{}{}\r\n", v.len() + 9, num, v);
            for (opt_v, num) in [
                (&self.lastname, "3000"),
                (&self.firstname, "3201"),
                (&self.patid, "3202"), 
                /* ... */
            ] {
                add_to_list(opt_v, num);

Thanks a lot for all the helpful hints. I will try it and I am shure this will solve the problem. Till now I didn't work with macros yet but this is a good moment to start with...

You can implement the Index and/or IndexMut traits and use these - also to implement the Iterator trait.
But you need to know all the fields.

The macro solution of @nerditation is working perfectly! Thanks

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.