From 41dae527bb065d4f0bbca32f4554913b08055868 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 11 Nov 2019 01:40:12 +0200 Subject: [PATCH 01/10] RFC: generic_derive --- text/0000-generic-derive.md | 269 ++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 text/0000-generic-derive.md diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md new file mode 100644 index 00000000000..04a46250ee9 --- /dev/null +++ b/text/0000-generic-derive.md @@ -0,0 +1,269 @@ +- Feature Name: `generic_derive` +- Start Date: 2019-11-09 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Add ability to pass generic parameters of the impl to the derive macros, +greatly increasing the flexibility of the `derive` attribute. + +# Motivation +[motivation]: #motivation + +Derive macros are a very convenient way to generating trait impls based on +the definition item of a type. However, the ability to use `#[derive(Trait)]` +is denied when the impl must have generic parameters that need to be defined +and bound in a more customized way than what the derive macro could generate +automatically based on the definition item of the `Self` type. + +Consider The Most Annoying Problem of `#[derive(Clone)]`: + +```rust +#[derive(Clone)] +pub struct WaitingForGodot { + // ... + _phantom_godot: PhantomData +} +``` + +The use of `derive` here is often a convenient pitfall that generates this impl: + +```rust +impl Clone for WaitingForGodot { + // ^---- Oops, did not really need this bound + // ... +} +``` + +This can be easily solved by customizing the impl parameter: + +```rust +#[derive( Clone)] +pub struct WaitingForGodot { + // ... + _phantom_godot: PhantomData +} +``` + +More traits could be made conveniently derivable with custom generics than +is feasible now: + +```rust +use derive_unpin::Unpin; + +#[derive( Unpin)] +pub struct MyFold { + #[unsafe_pinned] + stream: St, + #[unsafe_unpinned] + op: F, +} +``` + +In tandem with more elaborate helper attributes, it could be even more powerful: + +```rust +// A not-yet-written library providing the derive macro +use async_state_machine::Future; +use futures::future::{TryFuture, IntoFuture, MapOk}; + +#[derive( + < + Fut1: TryFuture, + Fut2: TryFuture, + F: FnOnce(::Ok) -> Fut2, + > Future +)] +enum AndThen { + First(MapOk), + #[after(First)] + #[future(output)] + Then(IntoFuture), +} +``` + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +The trait name in a `derive` attribute can be adorned with generic parameters +that specify the generics of the generated `impl` item: + +```rust +#[derive( Frob)] +struct Foo { + // ... +} +``` + +The derive macro for `Frob` is expected to generate an implementation item +with these generic parameters: + +```rust +impl Frob for Foo { + // ... +} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The syntax of an item in the `derive` attribute is extended to a subset of the +language that can occur in a trait implementation item between the keywords +`impl` and `for`: + +> _DeriveItem_ :\ +>    _Generics_? _TypePath_ + +The procedural macro can optionally support generic parameters to `derive` by +defining an entry point annotated with the `proc_macro_derive_with_generics` +attribute: + +```rust +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_derive_with_generics(Frob)] +pub fn derive_frob_with_generics( + generics: TokenStream, + trait_args: Option, + item: TokenStream, +) -> TokenStream { + // ... +} +``` + +Invoked in the example above, the function will receive the token stream of +`` as the first argument, a `Some` value with the token +stream of `` as the second argument, and the token stream with the +`struct Foo` item as the third. + +If the compiler does not find a matching `proc_macro_derive_with_generics` +symbol in the procedural macro crate that it has resolved for a `derive` item +that features generics, an error is reported stating that the macro does not +support generics. A plain old `derive` item can be processed with +a function annotated as `proc_macro_derive_with_generics` if no function +is annotated as `proc_macro_derive` for the same trait, otherwise the other +function gets called. + +# Drawbacks +[drawbacks]: #drawbacks + +This extension complicates the syntax of the `derive` attribute. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +Extending `derive` this way, we can solve its current shortcomings and +open it to more uses and experimentation. The proposed syntax should be +familiar to the developers, as it forms a part of the syntax of the intended +trait impl item. + +An [earlier proposal][rust-lang/rfcs#2353] to control generic bounds on +derived items introduces two attributes used on the generic parameters of +the type definition item, the whole item, or its fields. Using separate +attributes, however, visually distances the declaration from its effect +on the behavior on the `derive` attribute, and in many cases would be +more verbose. It also splits the solution across multiple attributes, whereas +the extended `derive` syntax proposed here is holistic, consistent with the +syntax of the generated impl item to the extent of being a literal +subsequence of it, and may allow further extension also in holistic ways. +The extension proposed here is opted into by the macro authors if and when +they wish to do so, while the solution proposed in RFC 2353 expects all +macro authors to implement support for the new attributes "so that a consistent +experience is maintained in the ecosystem". + +[rust-lang/rfcs#2353]: https://github.com/rust-lang/rfcs/pull/2353 + +An alternative has been proposed in the pre-RFC discussion to enable custom +bounds by trait-specific inert attributes. This has some disadvantages of +the alternative above, furthermore, it bifurcates the solution into mostly +similar custom attributes that add to cognitive load and may lead to +maintenance trouble if the preferred syntax is changed again. + +Everything proposed here is also possible to implement with custom attribute +macros instead of `derive` macros. But this would unnecessarily multiply +mechanisms for generating a trait implementation for a type. Plugging into a +well-defined syntax of the `derive` attribute would make the macro more +memorable for the users and may be more friendly to automatic analysis +than freeform attribute macros. + +# Prior art +[prior-art]: #prior-art + +The analysis done in the [previous proposal][rust-lang/rfcs#2353] is +sufficient for this RFC as well. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +## The syntax + +- Is it advisable, or even possible syntactically, to extend the general + `derive` syntax with optional generics for each comma-separated item, + or should this be only permitted as an alternative form of `derive` + with a single item? An alternative combining syntax + `#[derive( Trait1 + Trait2 + Trait3)]` is also possible, + either standalone or as an item in a comma-separated list. +- It's possible to extend the syntax even further by supporting a `where` + clause as a more readable alternative to bounds in the angle bracket syntax: + + ```rust + #[derive( Future) where + Fut1: TryFuture, + Fut2: TryFuture, + F: FnOnce(::Ok) -> Fut2, + ] + enum AndThen { + // ... + } + ``` + + The `where` clause syntax could be chosen as the only available way to + specify generics in preference to the angle bracketed parameter list. + If so, unbounded parameters would look a little weird, though permitted + in the current syntax for `where` clauses: + + ```rust + #[derive(Unwrap) where St: Unwrap, F:] + struct MyFold { + // ... + } + ``` + + This form would be also harder on the macro implementation, which would + not get a list of parameters to paste directly into the generated impl item, + but would have to assemble them from the type definition item and the + possible trait parameters. + +## The extended macro entry point + +- Should it be permitted to have two derive macros in scope for the + same trait, one with a `proc_macro_derive_with_generics` entry point + and the other with a plain `proc_macro_derive`? Conversely, should having + both kinds of entry points for the same trait in one procedural macro crate + be disallowed? +- Should the `proc_macro_derive` annotation be reused for the extended + function signature, rather than introducing `proc_macro_derive_with_generics` + and needing a policy on coexistence of the two kinds as per the questions + above (that is, disallow coexistence by uniting both kinds under a single + `proc_macro_derive` registry)? + This may lead to confusion, as the only distinguishing factor here would be + the number of parameters and their types, and two-to-three `TokenStream` + parameters do not exactly jump out and say "generics be here". A more + disciplined struct could be added to the `proc_macro` API for the new + function signature. + +# Future possibilities +[future-possibilities]: #future-possibilities + +Extending `derive` with generics would open this language extension mechanism +to far wider use and experimentation than what is possible today; the +[motivational section](#motivation) provides only a few beneficial examples. + +# Acknowledgements +[acknowledgements]: #acknowledgements + +Thanks to David Tolnay [@dtolnay](https://github.com/dtolnay) for suggesting +alternative ideas and offering constructive criticism. From 81f85feae5f663ef2288fe1419d58e45e69500df Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 11 Nov 2019 01:46:16 +0200 Subject: [PATCH 02/10] generic_derive: Assigned RFC 2811 --- text/0000-generic-derive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 04a46250ee9..b956570f6f0 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -1,6 +1,6 @@ - Feature Name: `generic_derive` - Start Date: 2019-11-09 -- RFC PR: +- RFC PR: [rust-lang/rfcs#2811](https://github.com/rust-lang/rfcs/pull/2811) - Rust Issue: # Summary From 103d55c3f2de89f946da2b3f19e13bff3fd1354b Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 11 Nov 2019 21:49:20 +0200 Subject: [PATCH 03/10] Small wording fix --- text/0000-generic-derive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index b956570f6f0..56bfbd87975 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -232,7 +232,7 @@ sufficient for this RFC as well. } ``` - This form would be also harder on the macro implementation, which would + This form would also be harder on the macro implementation, which would not get a list of parameters to paste directly into the generated impl item, but would have to assemble them from the type definition item and the possible trait parameters. From 2ef17002bdfca5dca8305d646952817c32a85806 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 11 Nov 2019 22:01:29 +0200 Subject: [PATCH 04/10] generic_derive: Fix the where clause form The attribute input is parsed as a DelimTokenTree; make the proposed syntax with where clauses conform to that. --- text/0000-generic-derive.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 56bfbd87975..714c7f55bc2 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -210,11 +210,11 @@ sufficient for this RFC as well. clause as a more readable alternative to bounds in the angle bracket syntax: ```rust - #[derive( Future) where + #[derive( Future where Fut1: TryFuture, Fut2: TryFuture, F: FnOnce(::Ok) -> Fut2, - ] + )] enum AndThen { // ... } @@ -226,7 +226,7 @@ sufficient for this RFC as well. in the current syntax for `where` clauses: ```rust - #[derive(Unwrap) where St: Unwrap, F:] + #[derive(Unwrap where St: Unwrap, F:)] struct MyFold { // ... } From eef30d96e1d474823c182c7214bc862bd14af655 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Mon, 11 Nov 2019 22:01:59 +0200 Subject: [PATCH 05/10] Mention that where clause is more expressive --- text/0000-generic-derive.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 714c7f55bc2..1ccf1c57b8b 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -207,7 +207,8 @@ sufficient for this RFC as well. `#[derive( Trait1 + Trait2 + Trait3)]` is also possible, either standalone or as an item in a comma-separated list. - It's possible to extend the syntax even further by supporting a `where` - clause as a more readable alternative to bounds in the angle bracket syntax: + clause, allowing more complex bounds, or just as a more readable alternative + to bounds in the angle bracket syntax: ```rust #[derive( Future where From eaaa257982dd9df946048beff273808e012fda1c Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 12 Nov 2019 00:24:57 +0200 Subject: [PATCH 06/10] generic_derive: Worked in the where clause There may be bounds that apply to types other than the generic parameters, so it makes sense to approve the where clause in this proposal. This calls for a struct to organize all the generic-carrying inputs to `proc_macro_derive_with_generics`, which was mentioned previously as a question. Name it DeriveGenerics. The extended derive syntax is now fully specified. It's now clear to me that adorning each comma-separated item with its own generic parameters is possible, but the where clause either needs ugly enclosing delimiters or can only come last. As it relates to some parameters, make it so that the where clause can only be appended to a single item. --- text/0000-generic-derive.md | 104 +++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 1ccf1c57b8b..cd94463ff5e 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -106,6 +106,17 @@ impl Frob for Foo { } ``` +A `where` clause is also permitted in the single-item form, allowing +bounds that do not apply directly to the parameters, or just as a more +readable alternative to giving bounds in the angle bracket syntax: + +```rust +#[derive( Frob where T: Bound1, Bar: Bound2)] +struct Foo { + // ... +} +``` + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -116,28 +127,56 @@ language that can occur in a trait implementation item between the keywords > _DeriveItem_ :\ >    _Generics_? _TypePath_ -The procedural macro can optionally support generic parameters to `derive` by +In the single-item form of the `derive` attribute, the item may be +appended by a `where` clause: + +> _DeriveAttrInputWithWhere_ :\ +>    _Generics_ _TypePath_ _WhereClause_ + +The overall `derive` attribute syntax is: + +> _DeriveAttrInput_:\ +>    _DeriveItem_ (`,` _DeriveItem_)\* `,`?\ +>    | _DeriveAttrInputWithWhere_ + +A procedural macro can optionally support generic parameters to `derive` by defining an entry point annotated with the `proc_macro_derive_with_generics` attribute: ```rust extern crate proc_macro; -use proc_macro::TokenStream; +use proc_macro::{DeriveGenerics, TokenStream}; #[proc_macro_derive_with_generics(Frob)] pub fn derive_frob_with_generics( - generics: TokenStream, - trait_args: Option, + generics: DeriveGenerics, item: TokenStream, ) -> TokenStream { // ... } ``` -Invoked in the example above, the function will receive the token stream of -`` as the first argument, a `Some` value with the token -stream of `` as the second argument, and the token stream with the -`struct Foo` item as the third. +The `DeriveGenerics` struct is provided by `proc_macro` as follows: + +```rust +pub struct DeriveGenerics { + /// List of impl parameters, including the enclosing angle brackets. + /// Empty if the derive input item has no generics. + pub impl_generics: TokenStream, + /// Generic arguments of the trait path including the angle brackets + /// or functional syntax, or empty if the trait has no generic parameters. + pub trait_args: TokenStream, + /// Where clause, if present. + pub where_clause: Option, +} +``` + +Invoked in the example featuring the `where` clause above, +the `DeriveGenerics` parameter of the function will receive: +- the token stream of `` in the `impl_generics` member, +- the token stream of `` in the `trait_args` member, +- `Some` with the token stream of `where T: Bound1, Bar: Bound2` + as the `where_clause` member. If the compiler does not find a matching `proc_macro_derive_with_generics` symbol in the procedural macro crate that it has resolved for a `derive` item @@ -157,8 +196,9 @@ This extension complicates the syntax of the `derive` attribute. Extending `derive` this way, we can solve its current shortcomings and open it to more uses and experimentation. The proposed syntax should be -familiar to the developers, as it forms a part of the syntax of the intended -trait impl item. +familiar to the developers, as it forms parts of the syntax of the intended +trait impl item. The same property makes the extended attribute input data +easier to use in the derive macros. An [earlier proposal][rust-lang/rfcs#2353] to control generic bounds on derived items introduces two attributes used on the generic parameters of @@ -167,8 +207,8 @@ attributes, however, visually distances the declaration from its effect on the behavior on the `derive` attribute, and in many cases would be more verbose. It also splits the solution across multiple attributes, whereas the extended `derive` syntax proposed here is holistic, consistent with the -syntax of the generated impl item to the extent of being a literal -subsequence of it, and may allow further extension also in holistic ways. +syntax of the generated impl item to the extent of informing literal parts +of it, and may allow further extension in similarly holistic ways. The extension proposed here is opted into by the macro authors if and when they wish to do so, while the solution proposed in RFC 2353 expects all macro authors to implement support for the new attributes "so that a consistent @@ -200,31 +240,15 @@ sufficient for this RFC as well. ## The syntax -- Is it advisable, or even possible syntactically, to extend the general - `derive` syntax with optional generics for each comma-separated item, - or should this be only permitted as an alternative form of `derive` - with a single item? An alternative combining syntax - `#[derive( Trait1 + Trait2 + Trait3)]` is also possible, - either standalone or as an item in a comma-separated list. -- It's possible to extend the syntax even further by supporting a `where` - clause, allowing more complex bounds, or just as a more readable alternative - to bounds in the angle bracket syntax: - - ```rust - #[derive( Future where - Fut1: TryFuture, - Fut2: TryFuture, - F: FnOnce(::Ok) -> Fut2, - )] - enum AndThen { - // ... - } - ``` - - The `where` clause syntax could be chosen as the only available way to +- A combining syntax `#[derive( Trait1 + Trait2 + Trait3)]` is also + possible, either standalone or as an item in a comma-separated list. + Should it be included while we are at radically extending `derive`, or + should it wait for another stabilization round just to be careful? +- The `where` clause syntax could be chosen as the only available way to specify generics in preference to the angle bracketed parameter list. If so, unbounded parameters would look a little weird, though permitted - in the current syntax for `where` clauses: + in the current syntax for `where` clauses (and hey, we have a chance to + legitimize smileys in Rust here): ```rust #[derive(Unwrap where St: Unwrap, F:)] @@ -250,11 +274,6 @@ sufficient for this RFC as well. and needing a policy on coexistence of the two kinds as per the questions above (that is, disallow coexistence by uniting both kinds under a single `proc_macro_derive` registry)? - This may lead to confusion, as the only distinguishing factor here would be - the number of parameters and their types, and two-to-three `TokenStream` - parameters do not exactly jump out and say "generics be here". A more - disciplined struct could be added to the `proc_macro` API for the new - function signature. # Future possibilities [future-possibilities]: #future-possibilities @@ -266,5 +285,6 @@ to far wider use and experimentation than what is possible today; the # Acknowledgements [acknowledgements]: #acknowledgements -Thanks to David Tolnay [@dtolnay](https://github.com/dtolnay) for suggesting -alternative ideas and offering constructive criticism. +Thanks to David Tolnay [@dtolnay](https://github.com/dtolnay) for proposing +the `where` clause, suggesting alternative ideas and offering constructive +criticism. From f1c4e45b3c382b69a858a9d21b90db91cb2cfed0 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Tue, 12 Nov 2019 12:52:35 +0200 Subject: [PATCH 07/10] generic_derive: "input item" is ambiguous --- text/0000-generic-derive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index cd94463ff5e..b3590076ed1 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -161,7 +161,7 @@ The `DeriveGenerics` struct is provided by `proc_macro` as follows: ```rust pub struct DeriveGenerics { /// List of impl parameters, including the enclosing angle brackets. - /// Empty if the derive input item has no generics. + /// Empty if the derive attribute item has no generics. pub impl_generics: TokenStream, /// Generic arguments of the trait path including the angle brackets /// or functional syntax, or empty if the trait has no generic parameters. From f6a53222d9b482c2e66e041e476014c6da4aee1e Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Wed, 13 Nov 2019 06:00:19 +0200 Subject: [PATCH 08/10] generic_derive: More realism to future example Add another magic helper attribute to the state machine future, to specify failure handling that needs to happen after the first step. --- text/0000-generic-derive.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index b3590076ed1..90e0c7718c6 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -77,6 +77,7 @@ use futures::future::{TryFuture, IntoFuture, MapOk}; > Future )] enum AndThen { + #[try_step] First(MapOk), #[after(First)] #[future(output)] From 38b4e013ab699c7969b13d447ffeaea7571e6f36 Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 14 Nov 2019 09:28:38 +0200 Subject: [PATCH 09/10] generic_derive: small rewording --- text/0000-generic-derive.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 90e0c7718c6..0fc593aca97 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -267,9 +267,9 @@ sufficient for this RFC as well. - Should it be permitted to have two derive macros in scope for the same trait, one with a `proc_macro_derive_with_generics` entry point - and the other with a plain `proc_macro_derive`? Conversely, should having - both kinds of entry points for the same trait in one procedural macro crate - be disallowed? + and the other with a plain `proc_macro_derive`? Conversely, should it be + disallowed to have both kinds of entry points for the same trait in one + procedural macro crate? - Should the `proc_macro_derive` annotation be reused for the extended function signature, rather than introducing `proc_macro_derive_with_generics` and needing a policy on coexistence of the two kinds as per the questions From 2265bcc6b97d9f2c5dbbbb476760b2b4cd3157cb Mon Sep 17 00:00:00 2001 From: Mikhail Zabaluev Date: Thu, 14 Nov 2019 10:00:25 +0200 Subject: [PATCH 10/10] Consider helper attribute approach as reconcilable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ¿Porque no los dos? --- text/0000-generic-derive.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/text/0000-generic-derive.md b/text/0000-generic-derive.md index 0fc593aca97..0463a6dc81f 100644 --- a/text/0000-generic-derive.md +++ b/text/0000-generic-derive.md @@ -211,17 +211,22 @@ the extended `derive` syntax proposed here is holistic, consistent with the syntax of the generated impl item to the extent of informing literal parts of it, and may allow further extension in similarly holistic ways. The extension proposed here is opted into by the macro authors if and when -they wish to do so, while the solution proposed in RFC 2353 expects all -macro authors to implement support for the new attributes "so that a consistent -experience is maintained in the ecosystem". +they wish to do so, while the solution proposed in [rust-lang/rfcs#2353] +expects all macro authors to implement support for the new attributes +"so that a consistent experience is maintained in the ecosystem". [rust-lang/rfcs#2353]: https://github.com/rust-lang/rfcs/pull/2353 -An alternative has been proposed in the pre-RFC discussion to enable custom -bounds by trait-specific inert attributes. This has some disadvantages of -the alternative above, furthermore, it bifurcates the solution into mostly -similar custom attributes that add to cognitive load and may lead to -maintenance trouble if the preferred syntax is changed again. +An alternative has been proposed in the pre-RFC discussion to customize +bounds by trait-specific helper attributes. This is already a practice in +some projects, including Servo. It has some disadvantages of the alternative +above, furthermore, it bifurcates the solution into mostly similar custom +attributes that add to cognitive load and may lead to maintenance trouble +if the preferred syntax is changed again. The proposal discussed here, however, +does not exclude augmentation with helper attributes, which may help further +reduce boilerplate in deriving traits within a large codebase, or in a +particularly popular API. A more systematic approach like all or part of +[rust-lang/rfcs#2353] is also not incompatible with this one. Everything proposed here is also possible to implement with custom attribute macros instead of `derive` macros. But this would unnecessarily multiply