|  | 
|  | 1 | +- Feature Name: `macro_derive` | 
|  | 2 | +- Start Date: 2024-09-20 | 
|  | 3 | +- RFC PR: [rust-lang/rfcs#3698](https://github.com/rust-lang/rfcs/pull/3698) | 
|  | 4 | +- Rust Issue: [rust-lang/rust#143549](https://github.com/rust-lang/rust/issues/143549) | 
|  | 5 | + | 
|  | 6 | +# Summary | 
|  | 7 | +[summary]: #summary | 
|  | 8 | + | 
|  | 9 | +Support implementing `derive(Trait)` via a `macro_rules!` macro. | 
|  | 10 | + | 
|  | 11 | +# Motivation | 
|  | 12 | +[motivation]: #motivation | 
|  | 13 | + | 
|  | 14 | +Many crates support deriving their traits with `derive(Trait)`. Today, this | 
|  | 15 | +requires defining proc macros, in a separate crate, typically with several | 
|  | 16 | +additional dependencies adding substantial compilation time, and typically | 
|  | 17 | +guarded by a feature that users need to remember to enable. | 
|  | 18 | + | 
|  | 19 | +However, many common cases of derives don't require any more power than an | 
|  | 20 | +ordinary `macro_rules!` macro. Supporting these common cases would allow many | 
|  | 21 | +crates to avoid defining proc macros, reduce dependencies and compilation time, | 
|  | 22 | +and provide these macros unconditionally without requiring the user to enable a | 
|  | 23 | +feature. | 
|  | 24 | + | 
|  | 25 | +The [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute) | 
|  | 26 | +crate defines proc macros that allow invoking declarative macros as derives, | 
|  | 27 | +demonstrating a demand for this. This feature would allow defining such derives | 
|  | 28 | +without requiring proc macros at all, and would support the same invocation | 
|  | 29 | +syntax as a proc macro. | 
|  | 30 | + | 
|  | 31 | +The derive feature of the crate has [various uses in the | 
|  | 32 | +ecosystem](https://github.com/search?q=macro_rules_attribute%3A%3Aderive&type=code). | 
|  | 33 | + | 
|  | 34 | +`derive` macros have a standard syntax that Rust users have come to expect for | 
|  | 35 | +defining traits; this motivates providing users a way to invoke that mechanism | 
|  | 36 | +for declarative macros. An attribute or a `macro_name!` invocation could serve | 
|  | 37 | +the same purpose, but that would be less evocative than `derive(Trait)` for | 
|  | 38 | +the purposes of making the purpose of the macro clear, and would additionally | 
|  | 39 | +give the macro more power to rewrite the underlying definition. Derive macros | 
|  | 40 | +simplify tools like rust-analyzer, which can know that a derive macro will | 
|  | 41 | +never change the underlying item definition. | 
|  | 42 | + | 
|  | 43 | +# Guide-level explanation | 
|  | 44 | +[guide-level-explanation]: #guide-level-explanation | 
|  | 45 | + | 
|  | 46 | +You can define a macro to implement `derive(MyTrait)` by defining a | 
|  | 47 | +`macro_rules!` macro with one or more `derive()` rules. Such a macro can create | 
|  | 48 | +new items based on a struct, enum, or union. Note that the macro can only | 
|  | 49 | +append new items; it cannot modify the item it was applied to. | 
|  | 50 | + | 
|  | 51 | +For example: | 
|  | 52 | + | 
|  | 53 | +```rust | 
|  | 54 | +trait Answer { fn answer(&self) -> u32; } | 
|  | 55 | + | 
|  | 56 | +macro_rules! Answer { | 
|  | 57 | +    // Simplified for this example | 
|  | 58 | +    derive() (struct $n:ident $_:tt) => { | 
|  | 59 | +        impl Answer for $n { | 
|  | 60 | +            fn answer(&self) -> u32 { 42 } | 
|  | 61 | +        } | 
|  | 62 | +    }; | 
|  | 63 | +} | 
|  | 64 | + | 
|  | 65 | +#[derive(Answer)] | 
|  | 66 | +struct Struct; | 
|  | 67 | + | 
|  | 68 | +fn main() { | 
|  | 69 | +    let s = Struct; | 
|  | 70 | +    assert_eq!(42, s.answer()); | 
|  | 71 | +} | 
|  | 72 | +``` | 
|  | 73 | + | 
|  | 74 | +Derive macros defined using `macro_rules!` follow the same scoping rules as | 
|  | 75 | +any other macro, and may be invoked by any path that resolves to them. | 
|  | 76 | + | 
|  | 77 | +A derive macro may share the same path as a trait of the same name. For | 
|  | 78 | +instance, the name `mycrate::MyTrait` can refer to both the `MyTrait` trait and | 
|  | 79 | +the macro for `derive(MyTrait)`. This is consistent with existing derive | 
|  | 80 | +macros. | 
|  | 81 | + | 
|  | 82 | +If a derive macro emits a trait impl for the type, it may want to add the | 
|  | 83 | +[`#[automatically_derived]`](https://doc.rust-lang.org/reference/attributes/derive.html#the-automatically_derived-attribute) | 
|  | 84 | +attribute, for the benefit of diagnostics. | 
|  | 85 | + | 
|  | 86 | +If a derive macro mistakenly emits the token stream it was applied to | 
|  | 87 | +(resulting in a duplicate item definition), the error the compiler emits for | 
|  | 88 | +the duplicate item should hint to the user that the macro was defined | 
|  | 89 | +incorrectly, and remind the user that derive macros only append new items. | 
|  | 90 | + | 
|  | 91 | +A `derive()` rule can be marked as `unsafe`: | 
|  | 92 | +`unsafe derive() (...) => { ... }`. | 
|  | 93 | +Invoking such a derive using a rule marked as `unsafe` | 
|  | 94 | +requires `unsafe` derive syntax: | 
|  | 95 | +`#[derive(unsafe(DangerousDeriveMacro))]` | 
|  | 96 | + | 
|  | 97 | +Invoking an unsafe derive rule without the unsafe derive syntax will produce a | 
|  | 98 | +compiler error. Using the unsafe derive syntax without an unsafe derive will | 
|  | 99 | +trigger an "unused unsafe" lint. (RFC 3715 defines the equivalent mechanism for | 
|  | 100 | +proc macro derives.) | 
|  | 101 | + | 
|  | 102 | +# Reference-level explanation | 
|  | 103 | +[reference-level-explanation]: #reference-level-explanation | 
|  | 104 | + | 
|  | 105 | +The grammar for macros is extended as follows: | 
|  | 106 | + | 
|  | 107 | +> _MacroRule_ :\ | 
|  | 108 | +>    ( `unsafe`<sup>?</sup> `derive` `(` `)` )<sup>?</sup>  _MacroMatcher_ `=>` _MacroTranscriber_ | 
|  | 109 | +
 | 
|  | 110 | +The _MacroMatcher_ matches the entire construct the attribute was | 
|  | 111 | +applied to, receiving precisely what a proc-macro-based attribute | 
|  | 112 | +would in the same place. | 
|  | 113 | + | 
|  | 114 | +(The empty parentheses after `derive` reserve future syntax space | 
|  | 115 | +for derives accepting arguments, at which time they'll be replaced | 
|  | 116 | +by a second _MacroMatcher_ that matches the arguments.) | 
|  | 117 | + | 
|  | 118 | +A derive invocation that uses an `unsafe derive` rule will produce | 
|  | 119 | +an error if invoked without using the `unsafe` derive syntax. A | 
|  | 120 | +derive invocation that uses an `derive` rule (without `unsafe`) | 
|  | 121 | +will trigger the "unused unsafe" lint if invoked using the `unsafe` | 
|  | 122 | +derive syntax. A single derive macro may have both `derive` and | 
|  | 123 | +`unsafe derive` rules, such as if only some invocations are unsafe. | 
|  | 124 | + | 
|  | 125 | +This grammar addition is backwards compatible: previously, a _MacroRule_ could | 
|  | 126 | +only start with `(`, `[`, or `{`, so the parser can easily distinguish rules | 
|  | 127 | +that start with `derive` or `unsafe`. | 
|  | 128 | + | 
|  | 129 | +Adding `derive` rules to an existing macro is a semver-compatible change, | 
|  | 130 | +though in practice, it will likely be uncommon. | 
|  | 131 | + | 
|  | 132 | +If a user invokes a macro as a derive and that macro does not have any `derive` | 
|  | 133 | +rules, the compiler should give a clear error stating that the macro is not | 
|  | 134 | +usable as a derive because it does not have any `derive` rules. | 
|  | 135 | + | 
|  | 136 | +# Drawbacks | 
|  | 137 | +[drawbacks]: #drawbacks | 
|  | 138 | + | 
|  | 139 | +This feature will not be sufficient for *all* uses of proc macros in the | 
|  | 140 | +ecosystem, and its existence may create social pressure for crate maintainers | 
|  | 141 | +to switch even if the result is harder to maintain. We can and should attempt | 
|  | 142 | +to avert and such pressure, such as by providing a post with guidance that | 
|  | 143 | +crate maintainers can link to when responding to such requests. | 
|  | 144 | + | 
|  | 145 | +Before stabilizing this feature, we should receive feedback from crate | 
|  | 146 | +maintainers, and potentially make further improvements to `macro_rules` to make | 
|  | 147 | +it easier to use for their use cases. This feature will provide motivation to | 
|  | 148 | +evaluate many new use cases that previously weren't written using | 
|  | 149 | +`macro_rules`, and we should consider quality-of-life improvements to better | 
|  | 150 | +support those use cases. | 
|  | 151 | + | 
|  | 152 | +# Rationale and alternatives | 
|  | 153 | +[rationale-and-alternatives]: #rationale-and-alternatives | 
|  | 154 | + | 
|  | 155 | +Adding this feature will allow many crates in the ecosystem to drop their proc | 
|  | 156 | +macro crates and corresponding dependencies, and decrease their build times. | 
|  | 157 | + | 
|  | 158 | +This will also give derive macros access to the `$crate` mechanism to refer to | 
|  | 159 | +the defining crate, which is simpler than mechanisms currently used in proc | 
|  | 160 | +macros to achieve the same goal. | 
|  | 161 | + | 
|  | 162 | +Macros defined this way can more easily support caching, as they cannot depend | 
|  | 163 | +on arbitrary unspecified inputs. | 
|  | 164 | + | 
|  | 165 | +Crates could instead define `macro_rules!` macros and encourage users to invoke | 
|  | 166 | +them using existing syntax like `macroname! { ... }`, rather than using | 
|  | 167 | +derives. This would provide the same functionality, but would not support the | 
|  | 168 | +same syntax people are accustomed to, and could not maintain semver | 
|  | 169 | +compatibility with an existing proc-macro-based derive. In addition, this would | 
|  | 170 | +not preserve the property derive macros normally have that they cannot change | 
|  | 171 | +the item they are applied to. | 
|  | 172 | + | 
|  | 173 | +A mechanism to define attribute macros would let people write attributes like | 
|  | 174 | +`#[derive_mytrait]`, but that would not provide compatibility with existing | 
|  | 175 | +derive syntax. | 
|  | 176 | + | 
|  | 177 | +We could allow `macro_rules!` derive macros to emit a replacement token stream. | 
|  | 178 | +That would be inconsistent with the restriction preventing proc macros from | 
|  | 179 | +doing the same, but it would give macros more capabilities, and simplify some | 
|  | 180 | +use cases. Notably, that would make it easy for derive macros to re-emit a | 
|  | 181 | +structure with another `derive` attached to it. | 
|  | 182 | + | 
|  | 183 | +We could allow directly invoking a `macro_rules!` derive macro as a | 
|  | 184 | +function-like macro. This has the potential for confusion, given the | 
|  | 185 | +append-only nature of derive macros versus the behavior of normal function-like | 
|  | 186 | +macros. It might potentially be useful for code reuse, however. | 
|  | 187 | + | 
|  | 188 | +## Syntax alternatives | 
|  | 189 | + | 
|  | 190 | +Rather than using `derive()` rules, we could have `macro_rules!` macros use a | 
|  | 191 | +`#[macro_derive]` attribute, similar to the `#[proc_macro_derive]` attribute | 
|  | 192 | +used for proc macros. | 
|  | 193 | + | 
|  | 194 | +However, this would be inconsistent with `attr()` rules as defined in RFC 3697. | 
|  | 195 | +This would also make it harder to add parameterized derives in the future (e.g. | 
|  | 196 | +`derive(MyTrait(params))`). | 
|  | 197 | + | 
|  | 198 | +# Prior art | 
|  | 199 | +[prior-art]: #prior-art | 
|  | 200 | + | 
|  | 201 | +We have had proc-macro-based derive macros for a long time, and the ecosystem | 
|  | 202 | +makes extensive use of them. | 
|  | 203 | + | 
|  | 204 | +The [`macro_rules_attribute`](https://crates.io/crates/macro_rules_attribute) | 
|  | 205 | +crate defines proc macros that allow invoking declarative macros as derives, | 
|  | 206 | +demonstrating a demand for this. This feature would allow defining such derives | 
|  | 207 | +without requiring proc macros at all, and would support the same invocation | 
|  | 208 | +syntax as a proc macro. | 
|  | 209 | + | 
|  | 210 | +The derive feature of the crate has [various uses in the | 
|  | 211 | +ecosystem](https://github.com/search?q=macro_rules_attribute%3A%3Aderive&type=code). | 
|  | 212 | + | 
|  | 213 | +# Unresolved questions | 
|  | 214 | +[unresolved-questions]: #unresolved-questions | 
|  | 215 | + | 
|  | 216 | +Before stabilizing this feature, we should ensure there's a mechanism macros | 
|  | 217 | +can use to ensure that an error when producing an impl does not result in a | 
|  | 218 | +cascade of additional errors caused by a missing impl. This may take the form | 
|  | 219 | +of a fallback impl, for instance. | 
|  | 220 | + | 
|  | 221 | +Before stabilizing this feature, we should make sure it doesn't produce wildly | 
|  | 222 | +worse error messages in common cases. | 
|  | 223 | + | 
|  | 224 | +Before stabilizing this feature, we should receive feedback from crate | 
|  | 225 | +maintainers, and potentially make further improvements to `macro_rules` to make | 
|  | 226 | +it easier to use for their use cases. This feature will provide motivation to | 
|  | 227 | +evaluate many new use cases that previously weren't written using | 
|  | 228 | +`macro_rules`, and we should consider quality-of-life improvements to better | 
|  | 229 | +support those use cases. | 
|  | 230 | + | 
|  | 231 | +Before stabilizing this feature, we should have clear public guidance | 
|  | 232 | +recommending against pressuring crate maintainers to adopt this feature | 
|  | 233 | +rapidly, and encourage crate maintainers to link to that guidance if such | 
|  | 234 | +requests arise. | 
|  | 235 | + | 
|  | 236 | +# Future possibilities | 
|  | 237 | +[future-possibilities]: #future-possibilities | 
|  | 238 | + | 
|  | 239 | +We should provide a way for derive macros to invoke other derive macros. The | 
|  | 240 | +`macro_rules_attribute` crate includes a `derive_alias` mechanism, which we | 
|  | 241 | +could trivially implement given a means of invoking another derive macro. | 
|  | 242 | + | 
|  | 243 | +We should provide a means to perform a `derive` on a struct without being | 
|  | 244 | +directly attached to that struct. (This would also potentially require | 
|  | 245 | +something like a compile-time reflection mechanism.) | 
|  | 246 | + | 
|  | 247 | +We could support passing parameters to derive macros (e.g. | 
|  | 248 | +`#[derive(Trait(params), OtherTrait(other, params))]`). This may benefit from | 
|  | 249 | +having `derive(...)` rules inside the `macro_rules!` macro declaration, similar | 
|  | 250 | +to the `attr(...)` rules proposed in RFC 3697. | 
|  | 251 | + | 
|  | 252 | +In the future, if we support something like `const Trait` or similar trait | 
|  | 253 | +modifiers, we'll want to support `derive(const Trait)`, and define how a | 
|  | 254 | +`macro_rules!` derive handles that. | 
|  | 255 | + | 
|  | 256 | +We should provide a way for `macro_rules!` macros to provide better error | 
|  | 257 | +reporting, with spans, rather than just pointing to the macro. | 
|  | 258 | + | 
|  | 259 | +We may want to support error recovery, so that a derive can produce an error | 
|  | 260 | +but still provide enough for the remainder of the compilation to proceed far | 
|  | 261 | +enough to usefully report further errors. | 
|  | 262 | + | 
|  | 263 | +As people test this feature and run into limitations of `macro_rules!` parsing, | 
|  | 264 | +we should consider additional features to make this easier to use for various | 
|  | 265 | +use cases. | 
|  | 266 | + | 
|  | 267 | +We could provide a macro matcher to match an entire struct field, along with | 
|  | 268 | +syntax (based on macro metavariable expressions) to extract the field name or | 
|  | 269 | +type (e.g. `${f.name}`). This would simplify many common cases by leveraging | 
|  | 270 | +the compiler's own parser. | 
|  | 271 | + | 
|  | 272 | +We could do the same for various other high-level constructs. | 
|  | 273 | + | 
|  | 274 | +We may want to provide simple helpers for generating/propagating `where` | 
|  | 275 | +bounds, which would otherwise be complex to do in a `macro_rules!` macro. | 
|  | 276 | + | 
|  | 277 | +We may want to add a lint for macro names, encouraging macros with derive rules | 
|  | 278 | +to use `CamelCase` names, and encouraging macros without derive rules to use | 
|  | 279 | +`snake_case` names. | 
|  | 280 | + | 
|  | 281 | +## Helper attribute namespacing and hygiene | 
|  | 282 | + | 
|  | 283 | +We should provide a way for derive macros to define helper attributes ([inert | 
|  | 284 | +attributes](https://doc.rust-lang.org/reference/attributes.html#active-and-inert-attributes) | 
|  | 285 | +that exist for the derive macro to parse and act upon). Such attributes are | 
|  | 286 | +supported by proc macro derives; however, such attributes have no namespacing, | 
|  | 287 | +and thus currently represent compatibility hazards because they can conflict. | 
|  | 288 | +We should provide a namespaced, hygienic mechanism for defining and using | 
|  | 289 | +helper attributes. | 
|  | 290 | + | 
|  | 291 | +For instance, could we have `pub macro_helper_attr! skip` in the standard | 
|  | 292 | +library, namespaced under `core::derives` or similar? Could we let macros parse | 
|  | 293 | +that in a way that matches it in a namespaced fashion, so that: | 
|  | 294 | +- If you write `#[core::derives::skip]`, the macro matches it | 
|  | 295 | +- If you `use core::derives::skip;` and `write #[skip]`, the macro matches it | 
|  | 296 | +- If you `use elsewhere::skip` (or no import at all) and write `#[skip]`, the | 
|  | 297 | +  macro *doesn't* match it. | 
|  | 298 | + | 
|  | 299 | +We already have *some* interaction between macros and name resolution, in order | 
|  | 300 | +to have namespaced `macro_rules!` macros. Would something like this be feasible? | 
|  | 301 | + | 
|  | 302 | +(We would still need to specify the exact mechanism by which macros match these | 
|  | 303 | +helper attributes.) | 
0 commit comments