ruststructmacroscompile-timegenerate

How do you generate a struct dynamically at compile time in Rust?


I have the following:

struct Health {
  health: f32,
}

struct Position {
  position: Vec2,
}

struct Collections {
  healths: Vec<Health>,
  positions: Vec<Position>,
}

I would like to generate the Collections struct automatically; I am thinking using a macro?

I thought perhaps I could mark each struct I want to include with a custom attribute and then have a macro which builds the Collections struct.

How could I do this?


Solution

  • To be able to do something like custom attributes you need to write a proc_macro, that can do almost anything you need with your code.

    For a simpler solution you may try with a normal macro_rules. For that you will need to enclose your type definitions into a macro that does the parsing, and emits back the type definition plus the extra code you need, in your case the Container class.

    Something like this:

    macro_rules! collectables {
        (
            $(
                #[collection=$fname:ident]
                $(#[$attr:meta])?
                $vis:vis struct $name:ident $def:tt
            )*
        ) => {
            // The struct definitions
            $(
                $(#[$attr])?
                $vis struct $name $def
            )*
            
            // The container
            #[derive(Default, Debug)]
            pub struct Collections {
              $(
                $fname: Vec<$name>,
              )*
            }
        };
    }
    

    Now you can use the macro to build your original code (playground):

    collectables!{
        #[collection=healths]
        #[derive(Debug)]
        struct Health {
          health: f32,
        }
        
        #[collection=positions]
        #[derive(Debug)]
        struct Position {
          position: (f32, f32),
        }
    }
    

    Note that as written the #[collection=xxx] attribute is mandatory and must be the first in every struct definition.