Skip to content

Commit 9ab1100

Browse files
committed
2025h1 project goal: declarative macro improvements
1 parent 259ebfc commit 9ab1100

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed

src/2025h1/macro-improvements.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# Declarative (`macro_rules!`) macro improvements
2+
3+
| Metadata | |
4+
| -------- | --------------|
5+
| Owner(s) | @joshtriplett |
6+
| Teams | [lang] |
7+
| Status | Proposed |
8+
9+
## Summary
10+
11+
In this project goal, I'll propose and shepherd Rust language RFCs to make
12+
`macro_rules!` macros just as capable as proc macros, and to make such macros
13+
easier to write. I'll also start prototyping extensions to the declarative
14+
macro system to make macros easier to write, with the aim of discussing and
15+
reaching consensus on those additional proposals during RustWeek (May 2025) at
16+
the latest. Finally, I'll write a series of Inside Rust blog posts on these
17+
features, to encourage crate authors to try them and provide feedback, and to
18+
plan transitions within the ecosystem.
19+
20+
The scope of this goal is an arc of many related RFCs that tell a complete
21+
story. The scope of this goal does not encompass the full implementation work,
22+
though I commit to spending time iterating on the design with potential
23+
implementers, and to shepherding and handling procedural matters (e.g.
24+
stabilization reports) as needed.
25+
26+
## Motivation
27+
28+
This project goal will make it possible, and straightforward, to write any type
29+
of macro using the declarative `macro_rules!` system. This will make many Rust
30+
projects build substantially faster, make macros simpler to write and
31+
understand, and reduce the dependency supply chain of most crates.
32+
33+
### The status quo
34+
35+
There are currently several capabilities that you can *only* get with a proc
36+
macro: defining an attribute macro that you can invoke with `#[mymacro]`, or
37+
defining a derive macro that you can invoke with `#[derive(MyTrait)]`. In
38+
addition, even without the requirement to do so (e.g. using workarounds such as
39+
the [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute)
40+
crate), macro authors often reach for proc macros anyway, in order to write
41+
simpler procedural code rather than refactoring it into a declarative form.
42+
43+
Proc macros are complex to build, have to be built as a separate crate that
44+
needs to be kept in sync with your main crate, add a heavy dependency chain
45+
(`syn`/`quote`/`proc-macro2`) to projects using them, add to build time, and
46+
lack some features of declarative (`macro_rules!`) macros such as `$crate`.
47+
48+
As a result, proc macros contribute to the perceptions that Rust is complex,
49+
has large dependency supply chains, and takes a long time to build. Crate
50+
authors sometimes push back on (or feature-gate) capabilities that require proc
51+
macros if their crate doesn't yet have a dependency on any, to avoid increasing
52+
their dependencies.
53+
54+
### The next 6 months
55+
56+
Over the next 6 months, I'll propose RFCs to improve the current state of
57+
declarative (`macro_rules!`) macros, and work to get those RFCs accepted. Those
58+
RFCs together will enable:
59+
60+
- Using `macro_rules!` to define attribute macros (`#[attr]`)
61+
- Using `macro_rules!` to define derive macros (`#[derive(Trait)]`)
62+
- Using `macro_rules!` to define unsafe attributes and unsafe derive macros.
63+
64+
I also have an RFC in progress ("macro fragment fields") to allow
65+
`macro_rules!` macros to better leverage the Rust parser for complex
66+
constructs. Over the next 6 months, I'll shepherd and refine that RFC, and
67+
design extensions of it to help parse additional constructs. (I expect this RFC
68+
to potentially require an additional design discussion before acceptance.) The
69+
goal will be to have enough capabilities to simplify many common cases of
70+
attribute macros and derive macros.
71+
72+
I'll propose initial prototypes of additional macro metavariable expressions to
73+
make `macro_rules!` easier to write, such as by handling multiple cases or
74+
iterating without having to recurse. This provides one of the key
75+
simplification benefits of proc macros, with minimal added complexity in the
76+
language. I expect these to reach pre-RFC form and be suitable for discussion
77+
at RustWeek in May 2025, and hopefully reach consensus, but I do not expect
78+
them to be fully accepted or shipped in the next 6 months.
79+
80+
There is already a wg-macros team. I've spoken with @eholk on that team about
81+
revitalizing that team, and considering policy and potential delegation from
82+
[lang], in a similar spirit to wg-const-eval, t-types, and t-opsem.
83+
84+
Much as with the const eval system, I expect this to be a long incremental
85+
road, with regular improvements to capabilities and simplicity. Crate authors
86+
can adopt new features as they arise, and transition from proc macros to
87+
declarative macros once they observe sufficient parity to support such a
88+
switch.
89+
90+
### The "shiny future" we are working towards
91+
92+
In the shiny future of Rust, the vast majority of crates don't need to use proc
93+
macros. They can easily implement attributes, derives, and complex macros using
94+
exclusively the declarative `macro_rules!` system.
95+
96+
Furthermore, crate authors will not feel compelled to use proc macros for
97+
simplicity, and will not have to contort their procedural logic in order to
98+
express it as a declarative macro macro. Crate authors will be able to write
99+
macros using `macro_rules!` in either a recursive or semi-procedural style. For
100+
instance, this could include constructs like `for` and `match`.
101+
102+
I expect that all of these will be available to macros written in any edition,
103+
though I also anticipate the possibility of syntax improvements unlocked by
104+
future editions or within future macro constructs. For instance, currently Rust
105+
macros do not reserve syntax like `$keyword` (e.g. `$for`). Existing editions
106+
could require the `${...}` macro metavariable syntax to introduce new
107+
constructs. Rust 2027 could reserve `$keyword`, and new syntax like `macro`
108+
could reserve such syntax in all editions.
109+
110+
## Design axioms
111+
112+
- Incremental improvements are often preferable to a ground-up rewrite. The
113+
ecosystem can adopt incremental improvements incrementally, and give feedback
114+
that inspires further incremental improvements.
115+
- There should never be a capability that *requires* using a proc macro.
116+
- The most obvious and simplest way to write a macro should handle all cases a
117+
user might expect to be able to write. Where possible, macros should
118+
automatically support new syntax variations of existing constructs, without
119+
requiring an update.
120+
- Macros should not have to recreate the Rust parser (or depend on crates that
121+
do so). Macros should be able to reuse the compiler's parser. Macros
122+
shouldn't have to parse an entire construct in order to extract one component
123+
of it.
124+
- Transforming iteration or matching into recursion is generally possible, but
125+
can sometimes obfuscate logic.
126+
127+
## Ownership and team asks
128+
129+
**Owner / Responsible Reporting Party:** @joshtriplett
130+
131+
| Subgoal | Owner(s) or team(s) | Notes |
132+
| ------------------------------------------------ | ------------------- | ----- |
133+
| Discussion and moral support | ![Team][] [lang] | |
134+
| Design for `macro_rules!` attributes/derives | | |
135+
| ↳ Author/revise/iterate RFCs | @joshtriplett | |
136+
| ↳ Prioritized nominations | ![Team][] [lang] | |
137+
| ↳ RFC decisions | ![Team][] [lang] | |
138+
| ↳ Iterate on design as needed | @joshtriplett | |
139+
| ↳ Author stabilization report (if ready) | @joshtriplett | |
140+
| ↳ Stabilization decision | ![Team][] [lang] | |
141+
| Policy and delegation improvements for wg-macros | @joshtriplett, [lang], @eholk, wg-macros | Discussed with @eholk; lang would decide whether to delegate specific matters to wg-macros |
142+
| Design and iteration for macro fragment fields | | |
143+
| ↳ Author initial RFC | @joshtriplett | |
144+
| ↳ Design meeting | ![Team][] [lang] | |
145+
| ↳ RFC decision | ![Team][] [lang] | |
146+
| ↳ Iterate on design as needed | @joshtriplett | |
147+
| ↳ Process feedback from crate authors | @joshtriplett | |
148+
| ↳ Support lang experiments for fragment fields | @joshtriplett | |
149+
| ↳ Author small RFCs for further fragment fields | @joshtriplett | |
150+
| ↳ RFC decisions | ![Team][] [lang] or wg-macros | |
151+
| Design for macro metavariable constructs | | |
152+
| ↳ Design research and discussions | @joshtriplett | |
153+
| ↳ Discussion and moral support | ![Team][] [lang] and wg-macros | |
154+
| ↳ Author initial RFC | @joshtriplett | |
155+
| Inside Rust blog post on attribute/derive macros | ![Team][] [lang] | |
156+
| ↳ Process feedback from blog post | @joshtriplett | |
157+
| Inside Rust blog post on additional capabilities | ![Team][] [lang] | |
158+
| ↳ Process feedback from blog post | @joshtriplett | |
159+
| Propose discussion session at RustWeek | @joshtriplett | |
160+
161+
### Definitions
162+
163+
Definitions for terms used above:
164+
165+
* *Discussion and moral support* is the lowest level offering, basically committing the team to nothing but good vibes and general support for this endeavor.
166+
* *Author RFC* and *Implementation* means actually writing the code, document, whatever.
167+
* *Design meeting* means holding a synchronous meeting to review a proposal and provide feedback (no decision expected).
168+
* *RFC decisions* means reviewing an RFC and deciding whether to accept.
169+
* *Org decisions* means reaching a decision on an organizational or policy matter.
170+
* *Secondary review* of an RFC means that the team is "tangentially" involved in the RFC and should be expected to briefly review.
171+
* *Stabilizations* means reviewing a stabilization and report and deciding whether to stabilize.
172+
* *Standard reviews* refers to reviews for PRs against the repository; these PRs are not expected to be unduly large or complicated.
173+
* *Prioritized nominations* refers to prioritized lang-team response to nominated issues, with the expectation that there will be *some* response from the next weekly triage meeting.
174+
* *Dedicated review* means identifying an individual (or group of individuals) who will review the changes, as they're expected to require significant context.
175+
* Other kinds of decisions:
176+
* [Lang team experiments](https://lang-team.rust-lang.org/how_to/experiment.html) are used to add nightly features that do not yet have an RFC. They are limited to trusted contributors and are used to resolve design details such that an RFC can be written.
177+
* Compiler [Major Change Proposal (MCP)](https://forge.rust-lang.org/compiler/mcp.html) is used to propose a 'larger than average' change and get feedback from the compiler team.
178+
* Library [API Change Proposal (ACP)](https://std-dev-guide.rust-lang.org/development/feature-lifecycle.html) describes a change to the standard library.
179+
180+
## Frequently asked questions
181+
182+
### What about "macros 2.0"
183+
184+
Whenever anyone proposes a non-trivial extension to macros, the question always
185+
arises of how it interacts with "macros 2.0", or whether it should wait for
186+
"macros 2.0".
187+
188+
"Macros 2.0" has come to refer to a few different things, ambiguously:
189+
190+
- Potential future extensions to declarative macros to improve
191+
hygiene/namespace handling.
192+
- An experimental marco system using the keyword `macro` that partially
193+
implements hygiene improvements and experimental alternate syntax, which
194+
doesn't have a champion or a path to stabilization, and hasn't seen active
195+
development in a long time.
196+
- A catch-all for hypothetical future macro improvements, with unbounded
197+
potential for scope creep.
198+
199+
As a result, the possibility of "macros 2.0" has contributed substantially to
200+
"stop energy" around improvements to macros.
201+
202+
This project goal takes the position that "macros 2.0" is sufficiently nebulous
203+
and unfinished that it should not block making improvements to the macro
204+
system. Improvements to macro hygiene should occur incrementally, and should
205+
not block other improvements.
206+
207+
### Could we support proc macros without a separate crate, instead?
208+
209+
According to reports from compiler experts, this would be theoretically
210+
possible but incredibly difficult, and is unlikely to happen any time soon. We
211+
shouldn't block on it.
212+
213+
In addition, this would not solve the problem of requiring proc macros to
214+
recreate the Rust parser (or depend on such a reimplementation).
215+
216+
### What about a "comptime" system?
217+
218+
This would likewise be possible in the future, but we shouldn't block on it.
219+
And as above, this would not solve the problem of requiring such a system to
220+
recreate the Rust parser. We would still need a design for allowing such
221+
comptime functions to walk the Rust AST in a forward-compatible way.

0 commit comments

Comments
 (0)