# Reading Club: Declarative (macro_rules!) macro improvements
- April 15 2025
- Participant: Vincent (@vincenzopalazzo), Josh (@joshtriplett), Eric (@eholk)
## Topic
Reading club about the rust project goal 2025
- Project Goals: https://rust-lang.github.io/rust-project-goals/2025h1/macro-improvements.html
- Tracking issue: https://github.com/rust-lang/rust-project-goals/issues/252
## Minutes
Josh: Main theme is letting you use declarative macros in more places that currently can only be done with proc macros for no particular reason.
Phase one is allowing defining attribute and derive macros, as well as unsafe variants, which proc macros also need.
Phase two adds macro fragment fields to make parsing easier.
Distant future adds things like `for` and `match` to macro syntax.
Vincent: I'm interested in implementing some of this stuff. It's useful in Linux kernel, but we don't like having to pull in dependencies like `syn`.
Josh: That's a good additional motivation. I was thinking mostly in terms of reducing the need for heavyweight dependencies, but this is also useful for even more constrained, no-dependency environments. The Linux people probably don't want to maintain a full Rust parser written in Rust.
Vincent: I was working on some of that, but macros are a better solution.
Regarding the project goal, is there something better to discuss during this reading club, or is the project goal doc the best one?
Josh: Project Goal doc is probably most helpful, but I also wrote a summary for a T-lang meeting recently. We should link to these RFCs in the summary.
RFCs:
Declarative macro_rules! attribute macros: https://github.com/rust-lang/rfcs/pull/3697
Declarative macro_rules! derive macros: https://github.com/rust-lang/rfcs/pull/3698
Unsafe derives and attributes: https://github.com/rust-lang/rfcs/pull/3715
*15 minutes to read derive macros RFC*
## Questions
### Syntax Bikeshed
Eric: What about defining derive macros as something like:
```rust
macro_rules! derive(Answer) {
(struct $n:ident $_:tt) => {
impl Answer for $n {
fn answer(&self) -> u32 { 42 }
}
};
}
```
I guess parsing would be tricky because `derive` is not a keyword, which means we'd need to decide whether to treat it as a contextual keyword based on whether it's followed by a `(`.
What are the tradeoffs?
Josh: For attribute macros and semver reasons we want to support both. E.g. `smol::main`, right now it's used as wrapper around your `main` function but there's an optional attribute version. With this design, we can support both in the same macro with the same name.
The exact syntax you suggested needs a place to put the arguments.
Eric: With derive arguments, you'll want to be able to match on different argument patterns.
### Can we mix derive and non-derive clauses in the same macro?
Eric: What happens if someone writes this?
```rust
macro_rules! Answer {
// Simplified for this example
derive() (struct $n:ident $_:tt) => {
impl Answer for $n {
fn answer(&self) -> u32 { 42 }
}
};
() => {
42
};
}
```
Would that allow someone to invoke `Answer!()` and have it expand to `42`?
It seems like this should be an error; either all your clauses are derives or none of them are.
The rest of the RFC makes it clear that this is meant to be allowed.
Josh: Yes, this is meant to be allowed. It's probably going to be uncommon, but there are cases that are useful.
### Implementation strategy
Vincent: Does it make more sense to start with the attribute RFC or this one?
Josh: I would hypothesize that starting with attributes will be easier, and if implemented while keeping derive in mind then derive should be a small delta on top.
Vincent: Yeah, the attribute one looks easier.
Josh: The difference hopefully comes down to adding a new variant to the rule enum, some extra lookup rules, and test cases.
This will have the added benefit of letting define a trait and its derive macro in the same crate.
--
Vincent: This isn't allow, right?
```rust
macro_rules! foo {
// Simplified for this example
derive() (struct $n:ident $_:tt) => {
impl Answer for $n {
fn answer(&self) -> u32 { 42 }
}
};
}
macro_rules! foo {
() => {
42
};
}
```
Josh: Correct, that would be a name conflict.
Josh: Need some tests for corner cases. For instance:
- declare a macro with only an attribute rule, attempt to call it with mac!(...)
- declare a macro with no attribute rules, attempt to call it as an attribute
- declare a macro with both attribute and non-attribute rules, invoke it as an attribute in a way that should fail with the attribute rule, but would match one of the non-attribute rules.
- declare a macro with both attribute and non-attribute rules, invoke it with `mac!(...)` in a way that should fail with the non-attribute rule, but would match one of the attribute rules.