Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Declarative macro_rules! derive macros #3698

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Changes from 3 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a3cd084
Declarative `macro_rules!` derive macros
joshtriplett Sep 21, 2024
567411b
Give a more realistic example
joshtriplett Sep 21, 2024
f3f5de9
Add alternative about allowing direct invocation
joshtriplett Sep 21, 2024
65b1053
Mention `$crate`
joshtriplett Sep 24, 2024
923325f
Caching
joshtriplett Sep 24, 2024
ba77548
Clarify text about helper attributes
joshtriplett Sep 24, 2024
8881b68
Future possibilities: Better error reporting
joshtriplett Sep 26, 2024
f195edf
Mention automatically_derived
joshtriplett Sep 26, 2024
c4b185b
Future possibilities: Error recovery
joshtriplett Sep 26, 2024
7bcdf3b
Add drawbacks section mentioning impact on crate maintainers
joshtriplett Sep 30, 2024
7e1d517
Expand future work
joshtriplett Sep 30, 2024
bf67d21
Future work: Namespacing helper attributes
joshtriplett Sep 30, 2024
32d91b6
Future work: helpers for `where` bounds
joshtriplett Sep 30, 2024
9faa8f4
Add unresolved question about avoid cascading errors due to missing i…
joshtriplett Sep 30, 2024
5ee8fe0
Add unresolved question to make sure we don't have awful error messages
joshtriplett Oct 2, 2024
8352b1c
Future work: helpers for higher-level concepts like struct fields
joshtriplett Oct 2, 2024
f797596
Add further steps about averting pressure on crate maintainers
joshtriplett Oct 21, 2024
3526d7f
Copy a drawback to the unresolved questions section
joshtriplett Oct 21, 2024
ba7effc
Fix typo
joshtriplett Oct 22, 2024
d4702b8
Future work: unsafe derives
joshtriplett Oct 22, 2024
aaf9860
Future possibilities: parameters
joshtriplett Oct 22, 2024
c6a2d35
Example
joshtriplett Oct 28, 2024
a71fbf7
Elaborate on an alternative
joshtriplett Nov 1, 2024
cf8e13e
Add more future possibilities
joshtriplett Nov 1, 2024
d50dbff
Further discussion on future possibility of derives with parameters
joshtriplett Nov 20, 2024
c323473
Discuss syntax alternative: `derive(...)` rules, like RFC 3697's `att…
joshtriplett Nov 20, 2024
e088d55
Unresolved question: helper attribute namespacing and hygiene
joshtriplett Nov 20, 2024
319ca10
Elaborate a future work item
joshtriplett Nov 20, 2024
5be851f
Future possibilities: const Trait and similar
joshtriplett Nov 20, 2024
07f297d
Updates from design meeting
joshtriplett Nov 21, 2024
0bb3ea4
Future work: talk about namespaced, hygienic helper attributes
joshtriplett Nov 21, 2024
d38892f
Future work: suggest a possible naming lint
joshtriplett Nov 21, 2024
cae86ff
Add unsafe derive rules and corresponding syntax
joshtriplett Nov 21, 2024
98aca06
Add reference-level explanation with grammar additions
joshtriplett Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions text/3698-declarative-derive-macros.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
- Feature Name: `declarative_derive_macros`
- Start Date: 2024-09-20
- RFC PR: [rust-lang/rfcs#3698](https://github.com/rust-lang/rfcs/pull/3698)
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)

# Summary
[summary]: #summary

Support implementing `derive(Trait)` via a `macro_rules!` macro.

# Motivation
[motivation]: #motivation

Many crates support deriving their traits with `derive(Trait)`. Today, this
requires defining proc macros, in a separate crate, typically with several
additional dependencies adding substantial compilation time, and typically
guarded by a feature that users need to remember to enable.

However, many common cases of derives don't require any more power than an
ordinary `macro_rules!` macro. Supporting these common cases would allow many
crates to avoid defining proc macros, reduce dependencies and compilation time,
and provide these macros unconditionally without requiring the user to enable a
feature.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

You can define a macro to implement `derive(MyTrait)` by defining a
`macro_rules!` macro with the `#[macro_derive]` attribute. Such a macro can
create new items based on a struct, enum, or union. Note that the macro can
only append new items; it cannot modify the item it was applied to.

For example:

```rust
trait Answer { fn answer(&self) -> u32; }

#[macro_derive]
macro_rules! Answer {
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
// Simplified for this example
(struct $n:ident $_:tt) => {
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
impl Answer for $n {
fn answer(&self) -> u32 { 42 }
}
};
}

#[derive(Answer)]
struct Struct;

fn main() {
let s = Struct;
assert_eq!(42, s.answer());
}
```

Derive macros defined using `macro_rules!` follow the same scoping rules as
any other macro, and may be invoked by any path that resolves to them.

A derive macro may share the same path as a trait of the same name. For
instance, the name `mycrate::MyTrait` can refer to both the `MyTrait` trait and
the macro for `derive(MyTrait)`. This is consistent with existing derive
macros.

A derive macro may also define *helper attributes*. These attributes are
[inert](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes),
and their only purpose is to be fed into the derive macro that defined them.
That said, they can be seen by all macros.

To define helper attributes, put an attributes key in the `macro_derive`
attribute, with a comma-separated list of identifiers for helper attributes:
`#[macro_derive(attributes(helper))]`. The derive macro can process the
`#[helper]` attribute, along with any arguments to it, as part of the item the
derive macro was applied to.
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved

If a derive macro mistakenly emits the token stream it was applied to
(resulting in a duplicate item definition), the error the compiler emits for
the duplicate item should hint to the user that the macro was defined
incorrectly, and remind the user that derive macros only append new items.
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved

# Rationale and alternatives
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
[rationale-and-alternatives]: #rationale-and-alternatives

Adding this feature will allow many crates in the ecosystem to drop their proc
macro crates and corresponding dependencies, and decrease their build times.
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved

Crates could instead define `macro_rules!` macros and encourage users to invoke
them using existing syntax like `macroname! { ... }`, rather than using
derives. This would provide the same functionality, but would not support the
same syntax people are accustomed to, and could not maintain semver
compatibility with an existing proc-macro-based derive. In addition, this would
not preserve the property derive macros normally have that they cannot change
the item they are applied to.

A mechanism to define attribute macros would let people write attributes like
`#[derive_mytrait]`, but that would not provide compatibility with existing
derive syntax.

We could allow `macro_rules!` derive macros to emit a replacement token stream;
however, that would be inconsistent with the restriction preventing proc macros
from doing the same.

We could allow directly invoking a `macro_rules!` derive macro as a
function-like macro. This has the potential for confusion, given the
append-only nature of derive macros versus the behavior of normal function-like
macros. It might potentially be useful for code reuse, however.

# Prior art
[prior-art]: #prior-art

We have had proc-macro-based derive macros for a long time, and the ecosystem
makes extensive use of them.

The [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute)
crate defines proc macros that allow invoking declarative macros as derives,
demonstrating a demand for this. This feature would allow defining such derives
without requiring proc macros at all, and would support the same invocation
syntax as a proc macro.

The `macro_derive` attribute and its `attributes` syntax are based on the
[existing `proc_macro_derive` attribute for proc
macros](https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros).
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved
joshtriplett marked this conversation as resolved.
Show resolved Hide resolved