Search code examples
rustrust-macros

Store state in macro_rules


I'd like to create a macro that operates on a given list of types, but I need to be able to store which other types are being processed.

A simple example of something I want to do:

struct Foo;

struct Bar {
   foo: Foo,
   data: u32,
}

baz!(Foo, Bar);

// outputs
struct OptFoo;

struct OptBar {
   foo: OptFoo,
   data: u32
}

The problem is that it doesn't seem like macro_rules allows me to store a temporary state (i.e a HashSet where I would tag which types are part of the macro invocation). The only workaround I have in mind is to write what I want as a proc_macro_derive and manually adding a custom attribute for each type I need but that's obviously far from perfect...

Edit:

The question is similar to this one, but here I'mm trying to save a state locally and temporarily (basically doing two passes over the arguments while storing data about those) in a single macro call. However it seems it's also not possible.


Solution

  • As pointed by @trentcl, what I want to achieve is indeed possible with proc macro (I thought proc macros were limited to Derive and attributes...)

    #[proc_macro]
    pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
        let input = proc_macro2::TokenStream::from(input);
    
        println!("{:?}", input);
    
        proc_macro::TokenStream::from(input)
    }
    
    generate!(struct Foo;);
    
    // outputs its argument without changing anything, i.e:
    // struct Foo ;
    

    The previous example demonstrates a trivial macro that prints to sdout the parsed input: TokenStream [Ident { ident: "struct", span: #0 bytes(330..336) }, Ident { ident: "Foo", span: #0 bytes(337..340) }, Punct { ch: ';', spacing: Alone, span: #0 bytes(340..341) }]

    Note that it parses the tokens but does not create an AST; we'll have to use syn for that.

    This repo has examples many of what can be done with proc macros, pretty helpful!