rust-lang / rust-lang/rfcs

macro_rules! should support gensym for creating items

Open
#1,266 26 comments 14 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

T-lang
Dominant language
Markdown
Stars
6.6k
Forks
1.7k
Avg merge
16h 14m
Merged PRs (30d)
1

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).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the macro_rules! gensym proposal and compare it with rust-lang/rust#19700. Define a backwards-compatible syntax for unique identifiers, then determine the implementation and testing work needed to support repeated macro invocations without the stated hygiene problems.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.