20

Is it possible to build a macro that doesn't output anything but instead for example stores state to build up a list and then a second macro that will then actually use that data and output some result?

For example:

trait SomeTrait {}

#[derive(mark)]
struct Person {}

impl SomeTrait for Person {}

#[derive(mark)]
struct Item {}

impl SomeTrait for Item  {}

#[derive(mark)]
struct Object {}

impl SomeTrait for Object {}

create_mapper!() // this then outputs the below function
//assuming for the fact that data is loaded correctly before this macro is used

fn select_item(kind: String) -> impl SomeTrait {
    match kind {
        "person" => Person,
        "item" => Item,
        "object" => Object,        
    }
}
Thermatix
  • 2,757
  • 21
  • 51
  • please be more specific, what do want to stock ? How do you want use it ? In definitive, a procedural macro can do more than macro, but are not magic, store a state need to stock it somewhere, static, const, something else. – Stargateur Oct 20 '18 at 23:26
  • You probably mean `match` instead of `map` in the code you would like to generate. You also need to add a catch-all branch. – Sven Marnach Oct 21 '18 at 09:13
  • Regarding your actual question, I'd keep it explicit and define an `enum` with the types. You could define a simple declarative macro to reduce the boilerplate. – Sven Marnach Oct 21 '18 at 09:15
  • Adding this here as I'm not sure this counts as an answer. Within the issue linked from @Lukas Kalbertodt answer at the end, someone posted this crate (https://crates.io/crates/macro_state) that seems to perform as requested, could this be the answer? – Thermatix Feb 10 '23 at 11:22

1 Answers1

18

Currently there is no officially supported way to store state that can be used by two different proc macro invocations. I created this very related issue where this problem is discussed.

Storing state is certainly possible, but just in a hacky way. You could, for example, serialize all your state into /tmp/my-state. Or you could try using static global variables. But even if this works now, this is not guaranteed to work in the future. Another problem: due to incremental compilation, it is not guaranteed that all of your proc macro invocations are actually executed. So if you have one macro that generates the state and one that reads it, if the first is not executed, really strange things happen. So it is technically possible to store global state, but it's not advisable.

In the issue linked above, you can see that MSleepyPanda proposed a possible solution, but we are far from having this implemented.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • AH, yes I did see that issue but I thought someone might have come up with a solution. – Thermatix Oct 21 '18 at 10:19
  • 3
    If you plan to serialize and store state on the filesystem, I strongly recommend storing it in `OUT_DIR` (by exporting the variable from a build script). Doing this will allow cargo to automatically clean up the state for you when `cargo clean` is invoked, it will also play nice with security tools that sandbox cargo. See my answer to a similar question here: https://stackoverflow.com/q/56215463/10126273 – Tenders McChiken Jun 07 '22 at 15:03