-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Currently macro_rules! doesn't support the ability to generate items with unique names. This generates an error that I'm defining struct Foo multiple times:
macro_rules! x {
() => {struct Foo;}
}
x! {}
x! {}The standard workaround is to accept an ident parameter in the macro, and name everything after that identifier somehow, even if only one item with that name is intended to be visible. lazy_static!, for instance, uses the ability of (non-tuple) structs to share a name with a value. Another option is to name a module after the ident and stuff everything inside that module. But these are incomplete workarounds: the former approach lets you create at most one type and at most one value, and the latter approach causes other hygiene problems because not everything expands the same inside a module. (For instance, ty parameters don't work right because paths to types aren't necessarily valid in the module.)
Having an actual gensym would solve this problem. I think you can do a simple syntax where a macro_rules! parameter of type gensym consumes no tokens and expands to a new, unique identifier per macro invocation, so you'd have e.g.
macro_rules! x {
($a:gensym) => {struct $a;}
}which could still be invoked as x! {}, but can be expanded multiple times, creating a unique structure each time. And you could do $a:gensym $b:gensym if you needed multiple names. If that's sensible, I can write this up more formally or attempt implementing it.
Note that this is different from rust-lang/rust#19700: that one's about hygiene for existing items invoked by the macro, not the creation of new items. (Also I think $crate is a sufficient workaround for that issue in practice, but doesn't help at all here.) However, the backwards-compatibility rationale in that issue applies: we can't simply gensym all literal item names in macros the way we do for let-bindings, since that would break macros that create items with specific names. So there needs to be specific syntax for gensym.
One answer is to put this off until macro! (or whatever you want to call macro_rules! 2.0), but if this can be done simply, clearly, and backwards-compatibly, it seems worth doing now, unless the new system is happening very soon (which it doesn't seem like it is).