From bb97d735b0b4b5493bf84fc996a937730c7877c7 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Sat, 4 Jun 2016 15:19:06 +0800 Subject: [PATCH 1/4] Add bang-auto-impls RFC --- text/0000-bang-auto-impls.md | 124 +++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 text/0000-bang-auto-impls.md diff --git a/text/0000-bang-auto-impls.md b/text/0000-bang-auto-impls.md new file mode 100644 index 00000000000..fe85f1424c8 --- /dev/null +++ b/text/0000-bang-auto-impls.md @@ -0,0 +1,124 @@ +- Feature Name: bang_auto_impls +- Start Date: 2016-06-04 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Make `!` automatically implement traits for which it has only a single possible +implementation. Add a `#[dont_impl_for_bang]` trait attribute to opt-out of +this behaviour. + +# Motivation +[motivation]: #motivation + +If a trait has no static methods or associated types then it has exactly one +possible implementation for the `!` type. To illustrate this, consider the +`Debug` trait and it's implementation for `!`. + +```rust +trait Debug { + fn fmt(&self, &mut Formatter) -> Result<(), fmt::Error> +} + +impl Debug for ! { + fn fmt(&self, _: &mut Formatter) -> Result<(), fmt::Error> { + *self + } +} +``` + +The important thing to note here is that it doesn't matter what we put in the +body of the `fmt` impl, because any method on `!` which takes a `self` can +never be called. For example, we could have also written the implementation as +this: + +```rust +impl Debug for ! { + fn fmt(&self, _: &mut Formatter) -> Result<(), fmt::Error> { + println!("Formatting a `!`"); + Ok(()) + } +} +``` + +But this implementation is exactly equivalent to the first - the entire method +body gets eliminated as dead code. + +Because of this, many traits - probably more than half in practice - have a +unique, trivial implementation for `!`. And this implementation can be +inferred. This RFC proposes that these implementations be automatically +inferred unless the user opts-out through an attribute on the trait. + +To see why this would be a valuable feature, consider the alternative where we +simply write out the impl wherever we want it. In the standard library we +might write out implementations of `Debug`, `Display` and `Error` as obvious +cases. But what about, say, `Hasher`? `Hasher` only has non-static methods, if +someone wants to use `!` where a `Hasher` trait bound is in force there's no +reason why it shouldn't work. In fact, half of the traits in the standard +library are like this. [This comment](https://github.com/rust-lang/rfcs/pull/1216#issuecomment-212265320) +lists most of them. + +Having to write out impls for `!` manually is a chore and will clutter code. +What's likely to happen in practice is that people won't think or won't bother +to impl their traits for `!` and then users of those traits won't be able to +use `!` even where it otherwise would make sense. + +# Detailed design +[design]: #detailed-design + +Any traits that have a unique, trivial implementation for `!` should have that +implementation automatically derived. This includes all traits *except*: + +* Traits which have a static method: + If a trait has a method which does not take a `self` then there may be many + non-equivalent ways to implement that method. +* Traits which have an associated type: + Because there are many possible choices for the type. + +## The `#[dont_impl_for_bang]` attribute + +Even where it's possible to infer the impl for `!` there may be cases where +people don't want this behaviour. For example, someone might define a marker +trait `trait Marker { }` whose purpose is to only include some small class of +types, not including `!`. For these cases this RFC proposes adding a +`#[dont_impl_for_bang]` trait attribute used like this: + +```rust +#[dont_impl_for_bang] +trait Marker { +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +* Add's more complexity to the language and compiler. +* People who aren't aware of this feature might be surprised to learn that their + trait implements `!`. In most cases this won't be a huge problem since their + trait *should* implement `!`, however in the cases where it shouldn't they + will need to know about the likely-to-remain-obscure `#[dont_impl_for_bang]` + attribute to avoid it. At any rate, `!` is already a rather surprising type in + that it can magically transform into other types under the right conditions. + This is possible essentially because there is exactly one possible + implementation of `Into` for `!` for all `T`, and the transformation only + occurs in dead code anyway. The author sees this RFC as a kind-of extension + of this behaviour. + +# Alternatives +[alternatives]: #alternatives + +* Not do this. +* Add an opt-in trait attribute instead. + Using a `#[derive_impl_for_bang]` attribute on traits which have an + inferable impl for `!` would be less cumbersome than writing these impls out + by hand. However it still comes with the problems of clutter and that most + traits could use this attribute but people won't think or won't bother to add + it. + +# Unresolved questions +[unresolved]: #unresolved-questions + +Is there a more sensible name than`#[dont_impl_for_bang]`? + From 2926a1f973ec9ef41540bb806e1371387b93521d Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 7 Jun 2016 19:16:57 +0800 Subject: [PATCH 2/4] Change opt-out syntax --- text/0000-bang-auto-impls.md | 45 ++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/text/0000-bang-auto-impls.md b/text/0000-bang-auto-impls.md index fe85f1424c8..6a204248c1d 100644 --- a/text/0000-bang-auto-impls.md +++ b/text/0000-bang-auto-impls.md @@ -7,8 +7,7 @@ [summary]: #summary Make `!` automatically implement traits for which it has only a single possible -implementation. Add a `#[dont_impl_for_bang]` trait attribute to opt-out of -this behaviour. +implementation. # Motivation [motivation]: #motivation @@ -77,48 +76,44 @@ implementation automatically derived. This includes all traits *except*: * Traits which have an associated type: Because there are many possible choices for the type. -## The `#[dont_impl_for_bang]` attribute +## Opting-out Even where it's possible to infer the impl for `!` there may be cases where people don't want this behaviour. For example, someone might define a marker trait `trait Marker { }` whose purpose is to only include some small class of -types, not including `!`. For these cases this RFC proposes adding a -`#[dont_impl_for_bang]` trait attribute used like this: +types, not including `!`. For these cases this RFC proposes allowing the +following to opt-out of automatically implementing a trait. ```rust -#[dont_impl_for_bang] -trait Marker { -} +impl !Marker for ! {} ``` # Drawbacks [drawbacks]: #drawbacks * Add's more complexity to the language and compiler. -* People who aren't aware of this feature might be surprised to learn that their - trait implements `!`. In most cases this won't be a huge problem since their - trait *should* implement `!`, however in the cases where it shouldn't they - will need to know about the likely-to-remain-obscure `#[dont_impl_for_bang]` - attribute to avoid it. At any rate, `!` is already a rather surprising type in - that it can magically transform into other types under the right conditions. - This is possible essentially because there is exactly one possible - implementation of `Into` for `!` for all `T`, and the transformation only - occurs in dead code anyway. The author sees this RFC as a kind-of extension - of this behaviour. +* People who aren't aware of this feature might be surprised to learn that + their trait implements `!`. In most cases this won't be a huge problem since + their trait *should* implement `!`, however in the cases where it shouldn't + they will need to know to opt-out. At any rate, `!` is already a rather + surprising type in that it can magically transform into other types under the + right conditions. This is possible essentially because there is exactly one + possible implementation of `Into` for `!` for all `T`, and the + transformation only occurs in dead code anyway. The author sees this RFC as + an extension in spirit of this behaviour. # Alternatives [alternatives]: #alternatives * Not do this. -* Add an opt-in trait attribute instead. - Using a `#[derive_impl_for_bang]` attribute on traits which have an - inferable impl for `!` would be less cumbersome than writing these impls out - by hand. However it still comes with the problems of clutter and that most - traits could use this attribute but people won't think or won't bother to add - it. +* Add a way to opt-in to derivining impls instead. Using a `#[derive_impl(!)]` + attribute on traits which have an inferable impl for `!` would be less + cumbersome than writing these impls out by hand. However it still comes with + the problems of clutter and that most traits could use this attribute but + people won't think or won't bother to add it. # Unresolved questions [unresolved]: #unresolved-questions -Is there a more sensible name than`#[dont_impl_for_bang]`? +* None known From dff7306d489804371d0c11a8165b084abf92f93a Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 7 Jun 2016 22:28:50 +0800 Subject: [PATCH 3/4] Minor correction --- text/0000-bang-auto-impls.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-bang-auto-impls.md b/text/0000-bang-auto-impls.md index 6a204248c1d..c756ae322fa 100644 --- a/text/0000-bang-auto-impls.md +++ b/text/0000-bang-auto-impls.md @@ -92,8 +92,8 @@ impl !Marker for ! {} [drawbacks]: #drawbacks * Add's more complexity to the language and compiler. -* People who aren't aware of this feature might be surprised to learn that - their trait implements `!`. In most cases this won't be a huge problem since +* People who aren't aware of this feature might be surprised to learn that `!` + implements their trait. In most cases this won't be a huge problem since their trait *should* implement `!`, however in the cases where it shouldn't they will need to know to opt-out. At any rate, `!` is already a rather surprising type in that it can magically transform into other types under the From d43c208bbafdb538c5aa24d32851c89f2c97c863 Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 7 Jun 2016 22:31:59 +0800 Subject: [PATCH 4/4] Another minor correction --- text/0000-bang-auto-impls.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-bang-auto-impls.md b/text/0000-bang-auto-impls.md index c756ae322fa..489883bd24d 100644 --- a/text/0000-bang-auto-impls.md +++ b/text/0000-bang-auto-impls.md @@ -93,11 +93,11 @@ impl !Marker for ! {} * Add's more complexity to the language and compiler. * People who aren't aware of this feature might be surprised to learn that `!` - implements their trait. In most cases this won't be a huge problem since - their trait *should* implement `!`, however in the cases where it shouldn't + implements their trait. In most cases this won't be a huge problem since `!` + *should* implement their trait, however in the cases where it shouldn't they will need to know to opt-out. At any rate, `!` is already a rather surprising type in that it can magically transform into other types under the - right conditions. This is possible essentially because there is exactly one + right conditions. This is possible essentially because there is exactly one possible implementation of `Into` for `!` for all `T`, and the transformation only occurs in dead code anyway. The author sees this RFC as an extension in spirit of this behaviour.