From 55de6a3a6d76c76d6ffa3ec29bee7c83512c796d Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Wed, 31 Aug 2016 14:19:10 +0200 Subject: [PATCH 01/24] Trait alias. --- text/0000-trait-alias.md | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 text/0000-trait-alias.md diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md new file mode 100644 index 00000000000..5471952e7de --- /dev/null +++ b/text/0000-trait-alias.md @@ -0,0 +1,70 @@ +- Feature Name: Trait alias +- Start Date: 2016-08-31 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +Traits can be aliased the same way types can be aliased with the `type` keyword. + +# Motivation +[motivation]: #motivation + +Sometimes, some traits are defined with parameters. For instance: + +``` +trait Foo { + // ... +} +``` + +It’s not uncommon to do that in *generic* crates and implement them in *backend* crates, where the +`T` template parameter gets substituted with a *backend* type. + +If someone wants to write `Foo` code and be compatible with all backends, then they will use the +*generic* crate’s `Foo<_>`. However, if someone wants to freeze the backend and keep using the same +one for the whole project but want to keep the ease of backend switching, a good practice is that +the backends should exporte a specific version of the trait so that it’s possible to use `Foo` +instead of the more explicit and unwanted `Foo`. + +# Detailed design +[design]: #detailed-design + +The idea is to add a new keyword or construct for enabling trait aliasing. One shouldn’t use the +`type` keyword as a trait is not a type and that could be very confusing. + +The `trait TraitAlias as Trait` is suggested as a starter construct for the discussion. + +``` +mod gen { + trait Foo { } +} + +mod backend_0 { + struct Bck0 {} + + trait Foo as gen::Foo; +} +``` + +# Drawbacks +[drawbacks]: #drawbacks + +The syntax `trait TraitAlias as Trait` makes parsers need a lookhead (`=` or `as`?). + +# Alternatives +[alternatives]: #alternatives + +A keyword was planned, like `alias`: + +``` +alias Foo = gen::Foo; +``` + +However, it’s not a good idea as it might clash with already used `alias` in codebases. + +# Unresolved questions +[unresolved]: #unresolved-questions + +The syntax `trait TraitAlias as Trait` is not yet stabilized and needs to be discussed. From c0c4834bf84c51b2ad77438437386cbd74f047ff Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Sat, 24 Dec 2016 14:04:46 +0100 Subject: [PATCH 02/24] Updated the proposal according to the discussion in #1733. --- text/0000-trait-alias.md | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 5471952e7de..c84d4da5973 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -13,7 +13,7 @@ Traits can be aliased the same way types can be aliased with the `type` keyword. Sometimes, some traits are defined with parameters. For instance: -``` +```rust trait Foo { // ... } @@ -34,20 +34,29 @@ instead of the more explicit and unwanted `Foo`. The idea is to add a new keyword or construct for enabling trait aliasing. One shouldn’t use the `type` keyword as a trait is not a type and that could be very confusing. -The `trait TraitAlias as Trait` is suggested as a starter construct for the discussion. +The `trait TraitAlias = Trait` was adopted as the syntax for aliasing. It creates a new trait alias +`TraitAlias` that will resolve to `Trait`. +```rust +trait TraitAlias = Debug; ``` -mod gen { - trait Foo { } -} -mod backend_0 { - struct Bck0 {} +Optionnaly, if needed, one can provide a `where` clause to express *bounds*: - trait Foo as gen::Foo; -} +```rust +trait TraitAlias = Debug where Self: Default; ``` +Trait aliasing to combinations of traits is also provided with the standard `+` construct: + +```rust +trait TraitAlias = Debug + Default; // same as the example above +``` + +Trait aliases can be used in any place arbitrary bounds would be syntactically legal. However, you +cannot use them in `impl` place but can have them as *trait objects*, in *where-clauses* and *type +parameters declarations* of course. + # Drawbacks [drawbacks]: #drawbacks From cf74fd5881c98e46a0d4ff9fc4600ac59fc2bba8 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Sun, 25 Dec 2016 02:21:23 +0100 Subject: [PATCH 03/24] Fixed typo. --- text/0000-trait-alias.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index c84d4da5973..90745939a23 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -41,7 +41,7 @@ The `trait TraitAlias = Trait` was adopted as the syntax for aliasing. It create trait TraitAlias = Debug; ``` -Optionnaly, if needed, one can provide a `where` clause to express *bounds*: +Optionally, if needed, one can provide a `where` clause to express *bounds*: ```rust trait TraitAlias = Debug where Self: Default; @@ -60,7 +60,7 @@ parameters declarations* of course. # Drawbacks [drawbacks]: #drawbacks -The syntax `trait TraitAlias as Trait` makes parsers need a lookhead (`=` or `as`?). +The syntax `trait TraitAlias = Trait` makes parsers need a lookhead. # Alternatives [alternatives]: #alternatives @@ -76,4 +76,4 @@ However, it’s not a good idea as it might clash with already used `alias` in c # Unresolved questions [unresolved]: #unresolved-questions -The syntax `trait TraitAlias as Trait` is not yet stabilized and needs to be discussed. +The syntax `trait TraitAlias = Trait` is not yet stabilized and needs to be discussed. From 042b8e47d5cb069033af56cf182a185739c90b57 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Wed, 15 Feb 2017 11:52:20 +0100 Subject: [PATCH 04/24] Strengthened the RFC with comments from people on the PR. --- text/0000-trait-alias.md | 125 +++++++++++++++++++++++++++++++++------ 1 file changed, 106 insertions(+), 19 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 90745939a23..e728b296e78 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -11,10 +11,12 @@ Traits can be aliased the same way types can be aliased with the `type` keyword. # Motivation [motivation]: #motivation +## First motivation: `impl` + Sometimes, some traits are defined with parameters. For instance: ```rust -trait Foo { +pub trait Foo { // ... } ``` @@ -22,35 +24,99 @@ trait Foo { It’s not uncommon to do that in *generic* crates and implement them in *backend* crates, where the `T` template parameter gets substituted with a *backend* type. -If someone wants to write `Foo` code and be compatible with all backends, then they will use the -*generic* crate’s `Foo<_>`. However, if someone wants to freeze the backend and keep using the same -one for the whole project but want to keep the ease of backend switching, a good practice is that -the backends should exporte a specific version of the trait so that it’s possible to use `Foo` -instead of the more explicit and unwanted `Foo`. +```rust +// in the backend crate +pub struct Backend; + +impl trait Foo for i32 { + // ... +} +``` + +Users who want to use that crate will have to export both the trait `Foo` from the generic crate +*and* the backend singleton type from the backend crate. Instead, we would like to be able to let +the backend singleton type hidden in the crate. The first shot would be to create a new trait for +our backend: + +```rust +pub trait FooBackend: Foo { + // ... +} + +fn use_foo(_: A) where A: FooBackend {} +``` + +If you try to pass an object that implements `Foo`, that won’t work, because it doesn’t +implement `FooBackend`. However, we can make it work with the following universal `impl`: + +```rust +impl FooBackend for T where T: Foo {} +``` + +With that, it’s now possible to pass an object that implements `Foo` to a function +expecting a `FooBackend`. However, what about impl blocks? What happens if we implement only +`FooBackend`? Well, we cannot, because the trait explicitely states that we need to implement +`Foo`. We hit a problem here. The problem is that even though there’s a compatibility at +the `trait bound` level between `Foo` and `FooBackend`, there’s none at the `impl` level, +so all we’re left with is implementing `Foo` – that will also provide an implementation for +`FooBackend` because of the universal implementation just above. + +## Second example: ergonomic collections and scrapping boilerplate + +Another example is associated types. Take the following [trait from tokio](https://docs.rs/tokio-service/0.1.0/tokio_service/trait.Service.html): + +```rust +pub trait Service { + type Request; + type Response; + type Error; + type Future: Future; + fn call(&self, req: Self::Request) -> Self::Future; +} +``` + +It would be nice to be able to create a few aliases to remove boilerplate for very common +combinations of associated types with `Service`. + +```rust +Service; +``` + +The trait above is a http service trait which only the associated type `Future` is left to be +implemented. Such an alias would be very appealing because it would remove copying the whole +`Service` trait into use sites – trait bounds, or even trait impls. Scrapping such an annoying +boilerplate is a definitive plus to the language and might be one of the most interesting use case. # Detailed design [design]: #detailed-design -The idea is to add a new keyword or construct for enabling trait aliasing. One shouldn’t use the -`type` keyword as a trait is not a type and that could be very confusing. +The syntax chosen to make a *trait alias* is: -The `trait TraitAlias = Trait` was adopted as the syntax for aliasing. It creates a new trait alias -`TraitAlias` that will resolve to `Trait`. +```rust +trait TraitAlias = Trait; +``` +It creates a new trait alias `TraitAlias` that will resolve to `Trait`. ```rust -trait TraitAlias = Debug; +trait DebugAlias = Debug; +``` + +Trait aliasing to combinations of traits is also provided with the standard `+` construct: + +```rust +trait DebugDefault = Debug + Default; ``` Optionally, if needed, one can provide a `where` clause to express *bounds*: ```rust -trait TraitAlias = Debug where Self: Default; +trait DebugDefault = Debug where Self: Default; // same as the example above ``` -Trait aliasing to combinations of traits is also provided with the standard `+` construct: +Furthermore, it’s possible to use only the `where` clause by using the special syntax `= _`: ```rust -trait TraitAlias = Debug + Default; // same as the example above +trait DebugDefault = _ where Self: Debug + Default; ``` Trait aliases can be used in any place arbitrary bounds would be syntactically legal. However, you @@ -65,15 +131,36 @@ The syntax `trait TraitAlias = Trait` makes parsers need a lookhead. # Alternatives [alternatives]: #alternatives -A keyword was planned, like `alias`: +It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl`: -``` -alias Foo = gen::Foo; +```rust +trait Foo {} + +trait FooFakeAlias: Foo {} + +impl Foo for T where T: FooFakeAlias {} ``` -However, it’s not a good idea as it might clash with already used `alias` in codebases. +This works for trait objects and trait bounds only. You cannot implement `FooFakeAlias` directly +because you need to implement `Foo` first – hence, you don’t really need `FooFakeAlias` if you can +implement `Foo`. + +There’s currently no alternative to the impl problem described here. # Unresolved questions [unresolved]: #unresolved-questions -The syntax `trait TraitAlias = Trait` is not yet stabilized and needs to be discussed. +What about bounds on type variable declaration in the trait alias? Consider the following: + +```rust +trait Foo = PartialEq +``` + +`PartialEq` has no super-trait `Bar`, but we’re adding one via our trait alias. What is the behavior +of such a feature? We can desugar it to: + +```rust +trait Foo = _ where Self: PartialEq, T: Bar +``` + +This semantic has to be discussed a bit more. From 88d3074957276c7201147fc625f18e0ebcecc1b9 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 6 Mar 2017 13:47:51 -0500 Subject: [PATCH 05/24] Update 0000-trait-alias.md Reorganize Detailed Design section, add some alternatives and unresolved questions. Still missing a teaching section. --- text/0000-trait-alias.md | 118 +++++++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e728b296e78..568354cd7fb 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -90,16 +90,13 @@ boilerplate is a definitive plus to the language and might be one of the most in # Detailed design [design]: #detailed-design +## Syntax + The syntax chosen to make a *trait alias* is: ```rust trait TraitAlias = Trait; ``` -It creates a new trait alias `TraitAlias` that will resolve to `Trait`. - -```rust -trait DebugAlias = Debug; -``` Trait aliasing to combinations of traits is also provided with the standard `+` construct: @@ -113,54 +110,103 @@ Optionally, if needed, one can provide a `where` clause to express *bounds*: trait DebugDefault = Debug where Self: Default; // same as the example above ``` -Furthermore, it’s possible to use only the `where` clause by using the special syntax `= _`: +Furthermore, it’s possible to use only the `where` clause by leaving the list of traits empty: ```rust -trait DebugDefault = _ where Self: Debug + Default; +trait DebugDefault = where Self: Debug + Default; ``` -Trait aliases can be used in any place arbitrary bounds would be syntactically legal. However, you -cannot use them in `impl` place but can have them as *trait objects*, in *where-clauses* and *type -parameters declarations* of course. +Specifically, the grammar being added is, in informal notation: -# Drawbacks -[drawbacks]: #drawbacks +``` +ATTRIBUTE* VISIBILITY? trait IDENTIFIER()? = GENERIC_BOUNDS (where PREDICATES)?; +``` -The syntax `trait TraitAlias = Trait` makes parsers need a lookhead. +`GENERIC_BOUNDS` is a list of zero or more traits and lifetimes separated by `+`, the same as the current syntax for bounds on a type parameter, and `PREDICATES` is a comma-separated list of zero or more predicates, just like any other `where` clause. A trait alias containing only lifetimes (`trait Static = 'static;`) is not allowed. -# Alternatives -[alternatives]: #alternatives +## Semantics -It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl`: +Trait aliases can be used in any place arbitrary bounds would be syntactically legal. -```rust -trait Foo {} +You cannot directly `impl` a trait alias, but can have them as *trait objects*, in *where-clauses* and *type +parameter declarations*. -trait FooFakeAlias: Foo {} +When using a trait alias as an object type, it is subject to object safety restrictions _after_ substituting the aliased traits. This means: -impl Foo for T where T: FooFakeAlias {} +1. It contains an object safe trait and zero or more of these other bounds: `Send`, `Sync`, `'static` (that is, `trait Show = Display + Debug;` would not be object safe). +2. All the associated types of the trait need to be specified. + +Some examples: + +```rust +trait Sink = Sync; +trait PrintableIterator = Display + Iterator; +trait IntIterator = Iterator; + +fn foo(...) { ... } // ok +fn baz1(x: Box) { ... } // ERROR: associated type not specified +fn baz2(x: Box>) { ... } // ok +fn baz3(x: Box) { ... } // ok (*) ``` -This works for trait objects and trait bounds only. You cannot implement `FooFakeAlias` directly -because you need to implement `Foo` first – hence, you don’t really need `FooFakeAlias` if you can -implement `Foo`. +The lines marked with `(*)` require [#24010](https://github.com/rust-lang/rust/issues/24010) to be fixed. -There’s currently no alternative to the impl problem described here. +# Drawbacks +[drawbacks]: #drawbacks -# Unresolved questions -[unresolved]: #unresolved-questions +- Adds another construct to the language. -What about bounds on type variable declaration in the trait alias? Consider the following: +- The syntax `trait TraitAlias = Trait` requires lookahead in the parser to disambiguate a trait from a trait alias. -```rust -trait Foo = PartialEq -``` +# Alternatives +[alternatives]: #alternatives -`PartialEq` has no super-trait `Bar`, but we’re adding one via our trait alias. What is the behavior -of such a feature? We can desugar it to: +- It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl`: + + ```rust + trait Foo {} + + trait FooFakeAlias: Foo {} + + impl Foo for T where T: FooFakeAlias {} + ``` + + This works for trait objects and trait bounds only. You cannot implement `FooFakeAlias` directly + because you need to implement `Foo` first – hence, you don’t really need `FooFakeAlias` if you can + implement `Foo`. + + There’s currently no alternative to the impl problem described here. + +- Similar to Haskell's ContraintKinds, we could declare an entire predicate as a reified list of constraints, instead of creating an alias for a set of supertraits and predicates. Syntax would be something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(...) where Foo { ... }` (i.e. direct substitution). Trait object usage is unclear. -```rust -trait Foo = _ where Self: PartialEq, T: Bar -``` +# Unresolved questions +[unresolved]: #unresolved-questions + +- Should we use `type` as the keyword instead of `trait`? + + `type Foo = Bar;` already creates an alias `Foo` that can be used as a trait object. + + If we used `type` for the keyword, this would imply that `Foo` could also be used as a bound as well. If we use `trait` as proposed in the body of the RFC, then `type Foo = Bar;` and `trait Foo = Bar;` _both_ create an alias for the object type, but only the latter creates an alias that can be used as a bound, which is a confusing bit of redundancy. + + However, this mixes the concepts of types and traits, which are different, and allows nonsense like `type Foo = Rc + f32;` to parse. + +- Which bounds need to be repeated when using a trait alias? + + [RFC 1927](https://github.com/rust-lang/rfcs/pull/1927) intends to change the rules here for traits, and we likely want to have the rules for trait aliases be the same to avoid confusion. + + The `contraint` alternative sidesteps this issue. + +- What about bounds on type variable declaration in the trait alias? Consider the following: + + ```rust + trait Foo = PartialEq; + ``` + + `PartialEq` has no super-trait `Bar`, but we’re adding one via our trait alias. What is the behavior + of such a feature? One possible desugaring is: + + ```rust + trait Foo = where Self: PartialEq, T: Bar; + ``` + -This semantic has to be discussed a bit more. From 819d8f91ae121d0217b06ddda94c0acc799eee39 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 6 Mar 2017 14:09:18 -0500 Subject: [PATCH 06/24] fix object safety section Minor updates. --- text/0000-trait-alias.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 568354cd7fb..12b95005fde 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -133,23 +133,27 @@ parameter declarations*. When using a trait alias as an object type, it is subject to object safety restrictions _after_ substituting the aliased traits. This means: -1. It contains an object safe trait and zero or more of these other bounds: `Send`, `Sync`, `'static` (that is, `trait Show = Display + Debug;` would not be object safe). +1. It contains an object safe trait, zero or more lifetimes, and zero or more of these other bounds: `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). 2. All the associated types of the trait need to be specified. +3. The `where` clause, if present, only contains bounds on `Self`. Some examples: ```rust trait Sink = Sync; -trait PrintableIterator = Display + Iterator; +trait ShareableIterator = Iterator + Sync; +trait PrintableIterator = Iterator + Display; trait IntIterator = Iterator; -fn foo(...) { ... } // ok -fn baz1(x: Box) { ... } // ERROR: associated type not specified -fn baz2(x: Box>) { ... } // ok -fn baz3(x: Box) { ... } // ok (*) +fn foo1(...) { ... } // ok +fn foo2>(...) { ... } // ok +fn bar1(x: Box) { ... } // ERROR: associated type not specified +fn bar2(x: Box>) { ... } // ok +fn bar3(x: Box) { ... } // ERROR: too many traits (*) +fn bar4(x: Box) { ... } // ok (*) ``` -The lines marked with `(*)` require [#24010](https://github.com/rust-lang/rust/issues/24010) to be fixed. +The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/rust/issues/24010) is fixed. # Drawbacks [drawbacks]: #drawbacks From 8a724714d9274bed7dfbd48e18661f0f1a6e6e95 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Mon, 6 Mar 2017 14:12:45 -0500 Subject: [PATCH 07/24] just one lifetime --- text/0000-trait-alias.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 12b95005fde..ec9f133b96a 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -133,7 +133,7 @@ parameter declarations*. When using a trait alias as an object type, it is subject to object safety restrictions _after_ substituting the aliased traits. This means: -1. It contains an object safe trait, zero or more lifetimes, and zero or more of these other bounds: `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). +1. It contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). 2. All the associated types of the trait need to be specified. 3. The `where` clause, if present, only contains bounds on `Self`. From 7b63d30616a78552d1be578727c95dd053afdb52 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 01:36:22 +0100 Subject: [PATCH 08/24] Added a teaching section and question about hidden free type variables. --- text/0000-trait-alias.md | 72 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index ec9f133b96a..cc55152be43 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -128,8 +128,7 @@ ATTRIBUTE* VISIBILITY? trait IDENTIFIER()? = GENERIC_BOUNDS (whe Trait aliases can be used in any place arbitrary bounds would be syntactically legal. -You cannot directly `impl` a trait alias, but can have them as *trait objects*, in *where-clauses* and *type -parameter declarations*. +You cannot directly `impl` a trait alias, but can have them as *bounds*, *trait objects* and *impl Trait*. When using a trait alias as an object type, it is subject to object safety restrictions _after_ substituting the aliased traits. This means: @@ -155,6 +154,59 @@ fn bar4(x: Box) { ... } // ok (*) The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/rust/issues/24010) is fixed. +# Teaching +[teaching]: #teaching + +[Traits](https://doc.rust-lang.org/book/traits.html) are obviously a huge prerequisite. Traits aliases could be introduced at the end of +that chapter. + +Conceptually, a *trait alias* is a syntax shortcut used to reason about one or more trait(s). Inherently, the *trait alias* is usable +in a limited set of places: + +- as a *bound*: exactly like a *trait*, a *trait alias* can be used to constraint a type (type parameters list, where-clause) +- as a *trait object*: same thing as with a *trait*, a *trait alias* can be used as a *trait object* if it fits object safety restrictions (see above in the [semantics](#semantics) section) +- in an [`impl Trait`](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md) + +Examples should be showed for all of the three cases above: + +#### As a bound + +```rust +trait StringIterator = Iterator; + +fn iterate(si: SI) where SI: StringIterator {} // used as bound +``` + +#### As a trait object + +```rust +fn iterate_object(si: &StringIterator) {} // used as trait object +``` + +#### In an `impl Trait` + +```rust +fn string_iterator_debug() -> impl Debug + Iterator {} // used in an impl Trait +``` + +As shown above, a *trait alias* can substitute associated types. It doesn’t have to substitute them all. In that case, the *trait alias* +is left incomplete and you have to pass it the associated types that are left. Example with the +[tokio case]([tokio example](#second-example-ergonomic-collections-and-scrapping-boilerplate): + +```rust +pub trait Service { + type Request; + type Response; + type Error; + type Future: Future; + fn call(&self, req: Self::Request) -> Self::Future; +} + +trait HttpService = Service; + +trait MyHttpService = HttpService; // assume MyFuture exists and fulfills the rules to be used in here +``` + # Drawbacks [drawbacks]: #drawbacks @@ -212,5 +264,21 @@ The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/ru ```rust trait Foo = where Self: PartialEq, T: Bar; ``` + +- Do we really want hidden free type variables? + + Consider the following: + ```rust + trait HttpService = Service; + ``` + + This trait has a hidden `Future` associated type that is left as free type variable. In order to use that `HttpService` trait, we need + to provide the `Future` type. Even though it’s always a good feature to have free associated types at the use site, I think it’s not + at the declaration site, and I’d be more willing to have the following syntax – which seems sounder and is more explicit of the interface of the trait alias because it requires to implement all associated types: + + ```rust + trait HttpService = Service; + ``` + Having to implement all associated types – with concrete types or type variables – is less confusing when we look at the definition of trait alias in my opinion. From 6601146417701594e29b089f09812ac11c6072d5 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 01:50:12 +0100 Subject: [PATCH 09/24] =?UTF-8?q?Fixed=20teaching=20example=20=E2=80=93=20?= =?UTF-8?q?spotted=20by=20@eddyb.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text/0000-trait-alias.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index cc55152be43..a3f5798d8da 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -186,7 +186,7 @@ fn iterate_object(si: &StringIterator) {} // used as trait object #### In an `impl Trait` ```rust -fn string_iterator_debug() -> impl Debug + Iterator {} // used in an impl Trait +fn string_iterator_debug() -> impl Debug + StringIterator {} // used in an impl Trait ``` As shown above, a *trait alias* can substitute associated types. It doesn’t have to substitute them all. In that case, the *trait alias* From b1be75b43e756bc376292834bb5dfcf08dbeca6d Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 02:13:05 +0100 Subject: [PATCH 10/24] Removed a question from the unresolved ones. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s actually simpler: what I talked about would add a new type variable that won’t be able to become a free type variable at any time. So it’s not the same thing. We have to cope with the free variables defined in the trait, and lookup at the documentation of the aliased trait to discover them all. --- text/0000-trait-alias.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index a3f5798d8da..d972d9de075 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -264,21 +264,3 @@ trait MyHttpService = HttpService; // assume MyFuture exists ```rust trait Foo = where Self: PartialEq, T: Bar; ``` - -- Do we really want hidden free type variables? - - Consider the following: - - ```rust - trait HttpService = Service; - ``` - - This trait has a hidden `Future` associated type that is left as free type variable. In order to use that `HttpService` trait, we need - to provide the `Future` type. Even though it’s always a good feature to have free associated types at the use site, I think it’s not - at the declaration site, and I’d be more willing to have the following syntax – which seems sounder and is more explicit of the interface of the trait alias because it requires to implement all associated types: - - ```rust - trait HttpService = Service; - ``` - - Having to implement all associated types – with concrete types or type variables – is less confusing when we look at the definition of trait alias in my opinion. From 90ce5c31b5d25e3d86b710f9c4dc3c88e5778212 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 16:01:12 +0100 Subject: [PATCH 11/24] Trait alias summary updated. --- text/0000-trait-alias.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index d972d9de075..e544d5e367c 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -6,7 +6,9 @@ # Summary [summary]: #summary -Traits can be aliased the same way types can be aliased with the `type` keyword. +Traits can be aliased with the `trait TraitAlias = …;` construct. Currently, the right hand side is +a bound – a single trait, a combination with `+` traits and lifetimes. Type parameters and +lifetimes can be added to the *trait alias* if needed. # Motivation [motivation]: #motivation From f04d4b29be028e5d17677960fc2415a22a6877e1 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 16:01:28 +0100 Subject: [PATCH 12/24] Moved trait alias type vs. trait keyword in alt. section. --- text/0000-trait-alias.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e544d5e367c..a1aafe31bf1 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -219,6 +219,14 @@ trait MyHttpService = HttpService; // assume MyFuture exists # Alternatives [alternatives]: #alternatives +- Should we use `type` as the keyword instead of `trait`? + + `type Foo = Bar;` already creates an alias `Foo` that can be used as a trait object. + + If we used `type` for the keyword, this would imply that `Foo` could also be used as a bound as well. If we use `trait` as proposed in the body of the RFC, then `type Foo = Bar;` and `trait Foo = Bar;` _both_ create an alias for the object type, but only the latter creates an alias that can be used as a bound, which is a confusing bit of redundancy. + + However, this mixes the concepts of types and traits, which are different, and allows nonsense like `type Foo = Rc + f32;` to parse. + - It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl`: ```rust @@ -239,15 +247,7 @@ trait MyHttpService = HttpService; // assume MyFuture exists # Unresolved questions [unresolved]: #unresolved-questions - -- Should we use `type` as the keyword instead of `trait`? - - `type Foo = Bar;` already creates an alias `Foo` that can be used as a trait object. - - If we used `type` for the keyword, this would imply that `Foo` could also be used as a bound as well. If we use `trait` as proposed in the body of the RFC, then `type Foo = Bar;` and `trait Foo = Bar;` _both_ create an alias for the object type, but only the latter creates an alias that can be used as a bound, which is a confusing bit of redundancy. - - However, this mixes the concepts of types and traits, which are different, and allows nonsense like `type Foo = Rc + f32;` to parse. - + - Which bounds need to be repeated when using a trait alias? [RFC 1927](https://github.com/rust-lang/rfcs/pull/1927) intends to change the rules here for traits, and we likely want to have the rules for trait aliases be the same to avoid confusion. From f269f9a6747eed82ad3664ee595a43afe380874c Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Tue, 7 Mar 2017 16:07:59 +0100 Subject: [PATCH 13/24] Added issue reference to trait alias RFC. --- text/0000-trait-alias.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index a1aafe31bf1..74cc04a321e 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -266,3 +266,5 @@ trait MyHttpService = HttpService; // assume MyFuture exists ```rust trait Foo = where Self: PartialEq, T: Bar; ``` + + [Issue 21903](https://github.com/rust-lang/rust/issues/21903) explains the same problem for type aliasing. From 3fc5568d8cb192afb266874c2930da0a3e61e888 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Wed, 8 Mar 2017 00:31:44 +0100 Subject: [PATCH 14/24] Fixed a typo in the teaching section of the trait-alias RFC. --- text/0000-trait-alias.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 74cc04a321e..abdc7e2dde8 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -193,7 +193,7 @@ fn string_iterator_debug() -> impl Debug + StringIterator {} // used in an impl As shown above, a *trait alias* can substitute associated types. It doesn’t have to substitute them all. In that case, the *trait alias* is left incomplete and you have to pass it the associated types that are left. Example with the -[tokio case]([tokio example](#second-example-ergonomic-collections-and-scrapping-boilerplate): +[tokio case](#second-example-ergonomic-collections-and-scrapping-boilerplate): ```rust pub trait Service { From 81825099a68c0735f852d38faf95f47624fd4564 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Wed, 22 Mar 2017 22:52:34 +0100 Subject: [PATCH 15/24] Hygiene. --- text/0000-trait-alias.md | 143 +++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 60 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index abdc7e2dde8..89ee8c8b61e 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -124,17 +124,23 @@ Specifically, the grammar being added is, in informal notation: ATTRIBUTE* VISIBILITY? trait IDENTIFIER()? = GENERIC_BOUNDS (where PREDICATES)?; ``` -`GENERIC_BOUNDS` is a list of zero or more traits and lifetimes separated by `+`, the same as the current syntax for bounds on a type parameter, and `PREDICATES` is a comma-separated list of zero or more predicates, just like any other `where` clause. A trait alias containing only lifetimes (`trait Static = 'static;`) is not allowed. +`GENERIC_BOUNDS` is a list of zero or more traits and lifetimes separated by `+`, the same as the +current syntax for bounds on a type parameter, and `PREDICATES` is a comma-separated list of zero or +more predicates, just like any other `where` clause. A trait alias containing only lifetimes (`trait +Static = 'static;`) is not allowed. ## Semantics Trait aliases can be used in any place arbitrary bounds would be syntactically legal. -You cannot directly `impl` a trait alias, but can have them as *bounds*, *trait objects* and *impl Trait*. +You cannot directly `impl` a trait alias, but can have them as *bounds*, *trait objects* and *impl +Trait*. -When using a trait alias as an object type, it is subject to object safety restrictions _after_ substituting the aliased traits. This means: +When using a trait alias as an object type, it is subject to object safety restrictions _after_ +substituting the aliased traits. This means: -1. It contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). +1. It contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: + `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). 2. All the associated types of the trait need to be specified. 3. The `where` clause, if present, only contains bounds on `Self`. @@ -154,19 +160,22 @@ fn bar3(x: Box) { ... } // ERROR: too many traits (*) fn bar4(x: Box) { ... } // ok (*) ``` -The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/rust/issues/24010) is fixed. +The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/rust/issues/24010) is +fixed. # Teaching [teaching]: #teaching -[Traits](https://doc.rust-lang.org/book/traits.html) are obviously a huge prerequisite. Traits aliases could be introduced at the end of -that chapter. +[Traits](https://doc.rust-lang.org/book/traits.html) are obviously a huge prerequisite. Traits +aliases could be introduced at the end of that chapter. -Conceptually, a *trait alias* is a syntax shortcut used to reason about one or more trait(s). Inherently, the *trait alias* is usable -in a limited set of places: +Conceptually, a *trait alias* is a syntax shortcut used to reason about one or more trait(s). +Inherently, the *trait alias* is usable in a limited set of places: -- as a *bound*: exactly like a *trait*, a *trait alias* can be used to constraint a type (type parameters list, where-clause) -- as a *trait object*: same thing as with a *trait*, a *trait alias* can be used as a *trait object* if it fits object safety restrictions (see above in the [semantics](#semantics) section) +- as a *bound*: exactly like a *trait*, a *trait alias* can be used to constraint a type (type + parameters list, where-clause) +- as a *trait object*: same thing as with a *trait*, a *trait alias* can be used as a *trait object* + if it fits object safety restrictions (see above in the [semantics](#semantics) section) - in an [`impl Trait`](https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md) Examples should be showed for all of the three cases above: @@ -191,9 +200,9 @@ fn iterate_object(si: &StringIterator) {} // used as trait object fn string_iterator_debug() -> impl Debug + StringIterator {} // used in an impl Trait ``` -As shown above, a *trait alias* can substitute associated types. It doesn’t have to substitute them all. In that case, the *trait alias* -is left incomplete and you have to pass it the associated types that are left. Example with the -[tokio case](#second-example-ergonomic-collections-and-scrapping-boilerplate): +As shown above, a *trait alias* can substitute associated types. It doesn’t have to substitute them +all. In that case, the *trait alias* is left incomplete and you have to pass it the associated types +that are left. Example with the [tokio case](#second-example-ergonomic-collections-and-scrapping-boilerplate): ```rust pub trait Service { @@ -214,57 +223,71 @@ trait MyHttpService = HttpService; // assume MyFuture exists - Adds another construct to the language. -- The syntax `trait TraitAlias = Trait` requires lookahead in the parser to disambiguate a trait from a trait alias. +- The syntax `trait TraitAlias = Trait` requires lookahead in the parser to disambiguate a trait + from a trait alias. # Alternatives [alternatives]: #alternatives -- Should we use `type` as the keyword instead of `trait`? - - `type Foo = Bar;` already creates an alias `Foo` that can be used as a trait object. - - If we used `type` for the keyword, this would imply that `Foo` could also be used as a bound as well. If we use `trait` as proposed in the body of the RFC, then `type Foo = Bar;` and `trait Foo = Bar;` _both_ create an alias for the object type, but only the latter creates an alias that can be used as a bound, which is a confusing bit of redundancy. - - However, this mixes the concepts of types and traits, which are different, and allows nonsense like `type Foo = Rc + f32;` to parse. - -- It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl`: - - ```rust - trait Foo {} - - trait FooFakeAlias: Foo {} - - impl Foo for T where T: FooFakeAlias {} - ``` - - This works for trait objects and trait bounds only. You cannot implement `FooFakeAlias` directly - because you need to implement `Foo` first – hence, you don’t really need `FooFakeAlias` if you can - implement `Foo`. - - There’s currently no alternative to the impl problem described here. - -- Similar to Haskell's ContraintKinds, we could declare an entire predicate as a reified list of constraints, instead of creating an alias for a set of supertraits and predicates. Syntax would be something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(...) where Foo { ... }` (i.e. direct substitution). Trait object usage is unclear. +## Should we use `type` as the keyword instead of `trait`? + +`type Foo = Bar;` already creates an alias `Foo` that can be used as a trait object. + +If we used `type` for the keyword, this would imply that `Foo` could also be used as a bound as +well. If we use `trait` as proposed in the body of the RFC, then `type Foo = Bar;` and +`trait Foo = Bar;` _both_ create an alias for the object type, but only the latter creates an alias +that can be used as a bound, which is a confusing bit of redundancy. + +However, this mixes the concepts of types and traits, which are different, and allows nonsense like +`type Foo = Rc + f32;` to parse. + +## Supertraits & universal `impl` + +It’s possible to create a new trait that derives the trait to alias, and provide a universal `impl` + +```rust +trait Foo {} + +trait FooFakeAlias: Foo {} + +impl Foo for T where T: FooFakeAlias {} +``` + +This works for trait objects and trait bounds only. You cannot implement `FooFakeAlias` directly +because you need to implement `Foo` first – hence, you don’t really need `FooFakeAlias` if you can +implement `Foo`. + +There’s currently no alternative to the impl problem described here. + +## `ContraintKinds` + +Similar to Haskell's ContraintKinds, we could declare an entire predicate as a reified list of +constraints, instead of creating an alias for a set of supertraits and predicates. Syntax would be +something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(...) where Foo { ... }` +(i.e. direct substitution). Trait object usage is unclear. # Unresolved questions [unresolved]: #unresolved-questions -- Which bounds need to be repeated when using a trait alias? - - [RFC 1927](https://github.com/rust-lang/rfcs/pull/1927) intends to change the rules here for traits, and we likely want to have the rules for trait aliases be the same to avoid confusion. - - The `contraint` alternative sidesteps this issue. - -- What about bounds on type variable declaration in the trait alias? Consider the following: - - ```rust - trait Foo = PartialEq; - ``` - - `PartialEq` has no super-trait `Bar`, but we’re adding one via our trait alias. What is the behavior - of such a feature? One possible desugaring is: - - ```rust - trait Foo = where Self: PartialEq, T: Bar; - ``` - - [Issue 21903](https://github.com/rust-lang/rust/issues/21903) explains the same problem for type aliasing. +## Which bounds need to be repeated when using a trait alias? + +[RFC 1927](https://github.com/rust-lang/rfcs/pull/1927) intends to change the rules here for traits, +and we likely want to have the rules for trait aliases be the same to avoid confusion. + +The `constraint` alternative sidesteps this issue. + +## What about bounds on type variable declaration in the trait alias? + +```rust +trait Foo = PartialEq; +``` + +`PartialEq` has no super-trait `Bar`, but we’re adding one via our trait alias. What is the behavior +of such a feature? One possible desugaring is: + +```rust +trait Foo = where Self: PartialEq, T: Bar; +``` + +[Issue 21903](https://github.com/rust-lang/rust/issues/21903) explains the same problem for type +aliasing. From 83322dea3dd6d66afdc86bbfb0c36c108d648f32 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Wed, 22 Mar 2017 23:34:05 +0100 Subject: [PATCH 16/24] Refactoring a little bit and added an unresolved question about trait aliases containing only lifetimes. --- text/0000-trait-alias.md | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 89ee8c8b61e..8b653490024 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -94,7 +94,9 @@ boilerplate is a definitive plus to the language and might be one of the most in ## Syntax -The syntax chosen to make a *trait alias* is: +### Declaration + +The syntax chosen to declare a *trait alias* is: ```rust trait TraitAlias = Trait; @@ -126,17 +128,14 @@ ATTRIBUTE* VISIBILITY? trait IDENTIFIER()? = GENERIC_BOUNDS (whe `GENERIC_BOUNDS` is a list of zero or more traits and lifetimes separated by `+`, the same as the current syntax for bounds on a type parameter, and `PREDICATES` is a comma-separated list of zero or -more predicates, just like any other `where` clause. A trait alias containing only lifetimes (`trait -Static = 'static;`) is not allowed. - -## Semantics +more predicates, just like any other `where` clause. -Trait aliases can be used in any place arbitrary bounds would be syntactically legal. +## Use semantics -You cannot directly `impl` a trait alias, but can have them as *bounds*, *trait objects* and *impl -Trait*. +You cannot directly `impl` a trait alias, but you can have them as *bounds*, *trait objects* and +*impl Trait*. -When using a trait alias as an object type, it is subject to object safety restrictions _after_ +When using a trait alias as a trait object, it is subject to object safety restrictions *after* substituting the aliased traits. This means: 1. It contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: @@ -259,9 +258,9 @@ implement `Foo`. There’s currently no alternative to the impl problem described here. -## `ContraintKinds` +## `ConstraintKinds` -Similar to Haskell's ContraintKinds, we could declare an entire predicate as a reified list of +Similar to GHC’s `ContraintKinds`, we could declare an entire predicate as a reified list of constraints, instead of creating an alias for a set of supertraits and predicates. Syntax would be something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(...) where Foo { ... }` (i.e. direct substitution). Trait object usage is unclear. @@ -269,6 +268,23 @@ something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(. # Unresolved questions [unresolved]: #unresolved-questions +## Trait alias containing only lifetimes + +This is annoying. Consider: + +```rust +trait Static = 'static; + +fn foo(t: T) where T: Static {} +``` + +Such an alias is legit. However, I feel concerned about the actual meaning of the declaration – i.e. +using the `trait` keyword to define alias on *lifetimes* seems a wrong design choice and seems not +very consistent. + +If we chose another keyword, like `constraint`, I feel less concerned and it would open further +opportunies – see the `ConstraintKinds` alternative discussion above. + ## Which bounds need to be repeated when using a trait alias? [RFC 1927](https://github.com/rust-lang/rfcs/pull/1927) intends to change the rules here for traits, From 310c7fefd861bb47cef66c454ff5f64d7b47c44b Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Thu, 23 Mar 2017 00:42:25 +0100 Subject: [PATCH 17/24] Proposal for pre-condition & implication bounds. --- text/0000-trait-alias.md | 62 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 8b653490024..a24a59b6750 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -135,13 +135,15 @@ more predicates, just like any other `where` clause. You cannot directly `impl` a trait alias, but you can have them as *bounds*, *trait objects* and *impl Trait*. +--- + When using a trait alias as a trait object, it is subject to object safety restrictions *after* substituting the aliased traits. This means: -1. It contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: - `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe). -2. All the associated types of the trait need to be specified. -3. The `where` clause, if present, only contains bounds on `Self`. +1. it contains an object safe trait, optionally a lifetime, and zero or more of these other bounds: + `Send`, `Sync` (that is, `trait Show = Display + Debug;` would not be object safe); +2. all the associated types of the trait need to be specified; +3. the `where` clause, if present, only contains bounds on `Self`. Some examples: @@ -307,3 +309,55 @@ trait Foo = where Self: PartialEq, T: Bar; [Issue 21903](https://github.com/rust-lang/rust/issues/21903) explains the same problem for type aliasing. + +**Note: what about the following proposal below?** + +When using a trait alias as a bound, you cannot add extra bound on the input parameters, like in the +following: + +```rust +trait Foo = PartialEq; +``` + +Here, `T` adds a `Bar` bound. Now consider: + +```rust +trait Bar = PartialEq; +``` + +Currently, we don’t have a proper interesting of that situation, because we’re adding in both cases +a bound, and we don’t know how to disambiguate between *pre-condition* and *implication*. That is, +is that added `Bar` bound a constraint that `T` must fulfil in order for the trait alias to be +met, or is it a constraint the trait alias itself adds? To disambiguate, consider: + +```rust +trait BarPrecond where T: Bar = PartialEq; +trait BarImplic = PartialEq where T: Bar; +trait BarImpossible where T: Bar = PartialEq where T: Bar; +``` + +`BarPrecond` would require the use-site code to fulfil the constraint, like the following: + +```rust +fn foo() where A: BarPrecond, T: Bar {} +``` + +`BarImplic` would give us `T: Bar`: + +```rust +fn foo() where A: BarImplic { + // T: Bar because given by BarImplic +} +``` + +`BarImpossible` wouldn’t compile because we try to express a pre-condition and an implication for +the same bound at the same time. However, it’d be possible to have both a pre-condition and an +implication on a parameter: + +```rust +trait BarBoth where T: Bar = PartialEq where T: Debug; + +fn foo() where A: BarBoth, T: Bar { + // T: Debug because given by BarBoth +} +``` From eb24a492d81411b7d55fc3719cf7a723879a4c0a Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Thu, 23 Mar 2017 00:43:58 +0100 Subject: [PATCH 18/24] Typo. let -> leave. --- text/0000-trait-alias.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index a24a59b6750..11af5567a2d 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -30,13 +30,13 @@ It’s not uncommon to do that in *generic* crates and implement them in *backen // in the backend crate pub struct Backend; -impl trait Foo for i32 { +impl Foo for i32 { // ... } ``` Users who want to use that crate will have to export both the trait `Foo` from the generic crate -*and* the backend singleton type from the backend crate. Instead, we would like to be able to let +*and* the backend singleton type from the backend crate. Instead, we would like to be able to leave the backend singleton type hidden in the crate. The first shot would be to create a new trait for our backend: From 4d99e23cf5065c989bd9f22b480a641ae50066b7 Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Thu, 23 Mar 2017 00:48:34 +0100 Subject: [PATCH 19/24] Syntax for Trait. --- text/0000-trait-alias.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index 11af5567a2d..b0f483418af 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -120,6 +120,21 @@ Furthermore, it’s possible to use only the `where` clause by leaving the list trait DebugDefault = where Self: Debug + Default; ``` +It’s also possible to partially bind associated types of the right hand side: + +```rust +trait IntoIntIterator = IntoIterator; +``` + +This would leave `IntoIntIterator` with a *free parameter* being `IntoIter`, and it should be bind +the same way associated types are bound with regular traits: + +```rust +fn foo(int_iter: I) where I: IntoIntIterator> {} +``` + +--- + Specifically, the grammar being added is, in informal notation: ``` From 54ca931480a93cbdd86b87c24c73d949aec4d99b Mon Sep 17 00:00:00 2001 From: Dimitri Sabadie Date: Thu, 23 Mar 2017 00:54:56 +0100 Subject: [PATCH 20/24] Typo. --- text/0000-trait-alias.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index b0f483418af..e289dd2310e 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -340,9 +340,9 @@ Here, `T` adds a `Bar` bound. Now consider: trait Bar = PartialEq; ``` -Currently, we don’t have a proper interesting of that situation, because we’re adding in both cases -a bound, and we don’t know how to disambiguate between *pre-condition* and *implication*. That is, -is that added `Bar` bound a constraint that `T` must fulfil in order for the trait alias to be +Currently, we don’t have a proper understanding of that situation, because we’re adding in both +cases a bound, and we don’t know how to disambiguate between *pre-condition* and *implication*. That +is, is that added `Bar` bound a constraint that `T` must fulfil in order for the trait alias to be met, or is it a constraint the trait alias itself adds? To disambiguate, consider: ```rust From bf3414d01d4508d484d004c99a85abd845473190 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 13 Apr 2017 11:21:54 +0200 Subject: [PATCH 21/24] expanded detailed design text to include explicit examples of parametric aliases. --- text/0000-trait-alias.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e289dd2310e..4df5d233a08 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -133,6 +133,14 @@ the same way associated types are bound with regular traits: fn foo(int_iter: I) where I: IntoIntIterator> {} ``` +A trait alias can be parameterized over types and lifetimes, just like traits themselves: + +```rust +trait LifetimeParametric<'a> = Iterator>;` + +trait TypeParametric = Iterator>; +``` + --- Specifically, the grammar being added is, in informal notation: @@ -144,6 +152,8 @@ ATTRIBUTE* VISIBILITY? trait IDENTIFIER()? = GENERIC_BOUNDS (whe `GENERIC_BOUNDS` is a list of zero or more traits and lifetimes separated by `+`, the same as the current syntax for bounds on a type parameter, and `PREDICATES` is a comma-separated list of zero or more predicates, just like any other `where` clause. +`GENERIC_PARAMS` is a comma-separated list of zero or more lifetime and type parameters, +with optional bounds, just like other generic definitions. ## Use semantics From 80dbc1304a534d558f10a47d83d0a9cc522ad22b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 13 Apr 2017 11:37:22 +0200 Subject: [PATCH 22/24] Explicitly point out alternatives to `trait Alias = where PREDICATES;` --- text/0000-trait-alias.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e289dd2310e..ee0dfdc0df9 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -282,6 +282,20 @@ constraints, instead of creating an alias for a set of supertraits and predicate something like `constraint Foo = T: Bar, Vec: Baz;`, used as `fn quux(...) where Foo { ... }` (i.e. direct substitution). Trait object usage is unclear. +## Syntax for sole `where` clause. + +The current RFC specifies that it is possible to use only the `where` clause by leaving the list of traits empty: + +```rust +trait DebugDefault = where Self: Debug + Default; +``` + +This is one of many syntaxes that are available for this construct. Alternatives include: + + * `trait DebugDefault where Self: Debug + Default;` (which has been [considered and discarded](https://github.com/rust-lang/rfcs/pull/1733#issuecomment-257993316) because [it might look](https://github.com/rust-lang/rfcs/pull/1733#issuecomment-258495468) too much like a new trait definition) + * `trait DebugDefault = _ where Self: Debug + Default;` (which was [considered and then removed](https://github.com/rust-lang/rfcs/pull/1733/commits/88d3074957276c7201147fc625f18e0ebcecc1b9#diff-ae27a1a8d977f731e67823349151bed5L116) because it is [technically unnecessary](https://github.com/rust-lang/rfcs/pull/1733#issuecomment-284252196)) + * `trait DebugDefault = Self where Self: Debug + Default;` (analogous to previous case but not formally discussed) + # Unresolved questions [unresolved]: #unresolved-questions From a2f8020e72eff390379fbf60dd4fff592f3c1f3a Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 13 Apr 2017 12:15:50 +0200 Subject: [PATCH 23/24] Add section pointing out how associated item ambiguity is handled. --- text/0000-trait-alias.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e289dd2310e..58a244a21cb 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -179,6 +179,28 @@ fn bar4(x: Box) { ... } // ok (*) The lines marked with `(*)` assume that [#24010](https://github.com/rust-lang/rust/issues/24010) is fixed. +### Ambiguous constraints + +If there are multiple associated types with the same name in a trait alias, +then it is a static error ("ambiguous associated type") to attempt to +constrain that associated type via the trait alias. For example: + +```rust +trait Foo { type Assoc; } +trait Bar { type Assoc; } // same name! + +// This works: +trait FooBar1 = Foo + Bar; + +// This does not work: +trait FooBar2 = Foo + Bar; +fn badness>() { } // ERROR: ambiguous associated type + +// Here are ways to workaround the above error: +fn better1>() { } // (leaves Bar::Assoc unconstrained) +fn better2 + Bar>() { } // constrains both +``` + # Teaching [teaching]: #teaching From dcec4077164dab2c2e10a2bee8666fe26c34d25d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 13 Apr 2017 12:51:22 +0200 Subject: [PATCH 24/24] Spell out that it is an error to override an equivalence constraint. --- text/0000-trait-alias.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/text/0000-trait-alias.md b/text/0000-trait-alias.md index e289dd2310e..cee2cdd8dd9 100644 --- a/text/0000-trait-alias.md +++ b/text/0000-trait-alias.md @@ -150,6 +150,22 @@ more predicates, just like any other `where` clause. You cannot directly `impl` a trait alias, but you can have them as *bounds*, *trait objects* and *impl Trait*. +---- + +It is an error to attempt to override a previously specified +equivalence constraint with a non-equivalent type. For example: + +```rust +trait SharableIterator = Iterator + Sync; +trait IntIterator = Iterator; + +fn quux1>(...) { ... } // ok +fn quux2>(...) { ... } // ok (perhaps subject to lint warning) +fn quux3>(...) { ... } // ERROR: `Item` already constrained + +trait FloIterator = IntIterator; // ERROR: `Item` already constrained +``` + --- When using a trait alias as a trait object, it is subject to object safety restrictions *after*