Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Trait alias. #1733

Merged
merged 30 commits into from Apr 24, 2017
Merged
Changes from 10 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
55de6a3
Trait alias.
hadronized Aug 31, 2016
c0c4834
Updated the proposal according to the discussion in #1733.
hadronized Dec 24, 2016
cf74fd5
Fixed typo.
hadronized Dec 25, 2016
042b8e4
Strengthened the RFC with comments from people on the PR.
hadronized Feb 15, 2017
88d3074
Update 0000-trait-alias.md
durka Mar 6, 2017
5d0b4fe
Merge pull request #1 from durka/patch-9
hadronized Mar 6, 2017
819d8f9
fix object safety section
durka Mar 6, 2017
8a72471
just one lifetime
durka Mar 6, 2017
554c4c1
Merge pull request #2 from durka/patch-10
hadronized Mar 6, 2017
7b63d30
Added a teaching section and question about hidden free type variables.
hadronized Mar 7, 2017
6601146
Fixed teaching example – spotted by @eddyb.
hadronized Mar 7, 2017
b1be75b
Removed a question from the unresolved ones.
hadronized Mar 7, 2017
90ce5c3
Trait alias summary updated.
hadronized Mar 7, 2017
f04d4b2
Moved trait alias type vs. trait keyword in alt. section.
hadronized Mar 7, 2017
f269f9a
Added issue reference to trait alias RFC.
hadronized Mar 7, 2017
3fc5568
Fixed a typo in the teaching section of the trait-alias RFC.
hadronized Mar 7, 2017
8182509
Hygiene.
hadronized Mar 22, 2017
83322de
Refactoring a little bit and added an unresolved question about trait…
hadronized Mar 22, 2017
310c7fe
Proposal for pre-condition & implication bounds.
hadronized Mar 22, 2017
eb24a49
Typo.
hadronized Mar 22, 2017
4d99e23
Syntax for Trait<Item = AssociatedType>.
hadronized Mar 22, 2017
54ca931
Typo.
hadronized Mar 22, 2017
bf3414d
expanded detailed design text to include explicit examples of paramet…
pnkfelix Apr 13, 2017
80dbc13
Explicitly point out alternatives to `trait Alias = where PREDICATES;`
pnkfelix Apr 13, 2017
a2f8020
Add section pointing out how associated item ambiguity is handled.
pnkfelix Apr 13, 2017
dcec407
Spell out that it is an error to override an equivalence constraint.
pnkfelix Apr 13, 2017
b5ff949
Merge pull request #3 from pnkfelix/add-parametric-trait-alias-examples
hadronized Apr 13, 2017
2b14c14
Merge pull request #4 from pnkfelix/alternatives-to-equals-where
hadronized Apr 13, 2017
5c94f49
Merge pull request #5 from pnkfelix/mult-unbound-assoc-types-with-sam…
hadronized Apr 13, 2017
2a1a5b2
Merge pull request #6 from pnkfelix/associated-type-rebinding
hadronized Apr 16, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
284 changes: 284 additions & 0 deletions text/0000-trait-alias.md
@@ -0,0 +1,284 @@
- 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this summary still accurate? I think it should be s/type keyword/trait keyword/, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial intention was to state that we can have alias the same way we already have with the type keyword and types. But you’re right, it’s confusing. I’m updating that.


# Motivation
[motivation]: #motivation

## First motivation: `impl`

Sometimes, some traits are defined with parameters. For instance:

```rust
pub trait Foo<T> {
// ...
}
```

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.

```rust
// in the backend crate
pub struct Backend;

impl trait Foo<Backend> for i32 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a typo, right? That is, It should just be impl Foo<Backend> for i32, not impl trait Foo, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// ...
}
```

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the english here. "We would like to be able to let the backend singleton type hidden in the crate" ??? Is there a wrong word or a missing verb somewhere there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I should have used “keep” instead of “let”?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, that would work. Or "leave" instead of "let" would also work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

our backend:

```rust
pub trait FooBackend: Foo<Backend> {
// ...
}

fn use_foo<A>(_: A) where A: FooBackend {}
```

If you try to pass an object that implements `Foo<Backend>`, that won’t work, because it doesn’t
implement `FooBackend`. However, we can make it work with the following universal `impl`:

```rust
impl<T> FooBackend for T where T: Foo<Backend> {}
```

With that, it’s now possible to pass an object that implements `Foo<Backend>` 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<Backend>`. We hit a problem here. The problem is that even though there’s a compatibility at
the `trait bound` level between `Foo<Backend>` and `FooBackend`, there’s none at the `impl` level,
so all we’re left with is implementing `Foo<Backend>` – 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<Item=Self::Response, Error=Self::Error>;
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<Request = http::Request, Response = http::Response, Error = http::Error>;
```

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

## Syntax

The syntax chosen to make a *trait alias* is:

```rust
trait TraitAlias = Trait;
```

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 DebugDefault = Debug where Self: Default; // same as the example above
```

Furthermore, it’s possible to use only the `where` clause by leaving the list of traits empty:

```rust
trait DebugDefault = where Self: Debug + Default;
```

Specifically, the grammar being added is, in informal notation:

```
ATTRIBUTE* VISIBILITY? trait IDENTIFIER(<GENERIC_PARAMS>)? = GENERIC_BOUNDS (where PREDICATES)?;
Copy link
Member

@pnkfelix pnkfelix Mar 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read the whole Syntax section, and failed to see explicit mention of support for the Trait<Item=Type> syntax.

I infer that you want to support this syntax from the Motivation section of the RFC, specifically from your second example with the Service trait that has some specific concrete types already instantiated for some of the associated types.

(If I recall correctly, Rust's current where clauses cannot express type equality constraints, so this really is more than just sugar on top of what you've already specified here with where PREDICATES)

  • I guess you did say "the same as the current syntax for bounds on a type parameter", which arguably should be enough to imply you intend for Trait<Item=Type> to be supported. Still, an explicit example would be appreciated here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

```

`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*.

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).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote in hadronized#2 (comment):

Oh, should also specify what happens if I have trait Foo<'a> = Bar + 'a; and I use Foo + 'b as a trait object.
It has to either be an error or require that 'b outlive 'a (i.e. + 'static is fine, shorter than 'a isn't).

The former is the conservative approach, the latter would be compatible with today's trait definitions.

2. All the associated types of the trait need to be specified.
3. The `where` clause, if present, only contains bounds on `Self`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote in hadronized#2 (comment):

I'd just literally write something like ", which are considered part of supertraits, and subject to the same restrictions in 1."


Some examples:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume your system is meant to handle the case where a trait alias has specified some of the associated types, and you instantiate that alias supplying others.

I would like examples that explicitly points out how such cases are handled. E.g. give us a trait defintion with two associated types, an alias that defines one of them, and then various uses of the alias.

In particular:

  1. One example should show adding the second associated type when using the alias

  2. Other example(s) should answer the questions: What happens if I instantiate the previously bound associated type with the same concrete type? What about if I instantiate it with a different concrete type?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(its possible that you actually covered some of this material in your further discussion of the Service example in the "Teaching" section of the RFC. Nonetheless, I think the answers deserve explicit treatment here.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume your system is meant to handle the case where a trait alias has specified some of the associated types, and you instantiate that alias supplying others.

I would not expect this to work, actually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we mean by 'instantiate'? If it means implement, we don't allow implementing aliases under this RFC. If it means something like this:

trait Foo {
   type A1;
  type A2;
}
trait Bar = Foo<A1 = ()>;
trait Baz = Bar<A2 = ()>;

I have not formed an opinion about whether that should work or not.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@withoutboats I think it means something like this:

trait Foo {
    type A1;
    type A2;
}
trait Bar = Foo<A1 = ()>;
// later, use `Bar<A2 = i32>` or just `Bar` if you don't need to constrain `Foo::A2` in your bound

Copy link
Member

@pnkfelix pnkfelix Mar 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@solson yes I meant an example like your first one, i.e.:

trait Foo { type A1; type A2; }
trait Bar = Foo<A1 = ()>; // is it okay to specify some associated types here ...
fn quux<T: Bar<A2=i32>(t: T) { ... } // ... and others here?

fn hoho<T: Bar<A1=f64, A2=()>>(t: T) { ... } // furthermore, can I revise the choice?

Copy link
Member

@pnkfelix pnkfelix Mar 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A related question: You show some trait aliases where the RHS supplies inputs for the trait parameters. And you show others where the trait parameter inputs are not supplied.

  • Update: I was mistaken; I do not actually see any examples where the trait parameters are left unsupplied. I assume that means it is not legal, which makes me happier.

Do you want to support supplying only a prefix of the inputs?

(I assume the answer is no, at least for this first version of the feature, but this should be made explicit.)

trait Inputs<X, Y> { } // I have two inputs
trait Alias1 = Inputs; // This is legal by the current RFC (update: or maybe its not?)
trait Alias2 = Inputs<i32, i32>; // and this is also legal
trait Alias3 = Inputs<i32>; // but I assume this is not legal ...
trait Alias4<X> = Input<i32, X>; // ... and should instead be explicitly encoded like this (right?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@withoutboats I think there is a variant of the example you wrote down (where a series of trait aliases incrementally adds more constraints on the type parameters) actually already present in the RFC, down in the MyHttpService example of the teaching section. Am I misunderstanding you (or the RFC)?

(All the more reason that this detail of the feature needs to be spelled out explicitly up here...)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to implement equality constraints in full generality before this is stabilized? That might allow this to be more conservative while keeping full expressive power.

I'm definitely against missing arguments being hacked to work. I'm also against (though less vehemently) allowing "equality constraint arguments" to aliases for now—just like impls of aliases, it's an idea that doesn't scale from single traits to arbitrary bounds.

Trait<A1=...> + Trait<A2=...> is probably fine though.

Copy link
Contributor

@durka durka Mar 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A trait alias which partially applies the associated types of its supertrait(s) seems useful. Allowing a user of the trait alias to change those applications does not appeal to me, because then the alias definition is anti-self-documenting. You should make a separate alias without the associated types filled in if you intend it to be used that way.


```rust
trait Sink = Sync;
trait ShareableIterator = Iterator + Sync;
trait PrintableIterator = Iterator<Item=i32> + Display;
trait IntIterator = Iterator<Item=i32>;

fn foo1<T: ShareableIterator>(...) { ... } // ok
fn foo2<T: ShareableIterator<Item=i32>>(...) { ... } // ok
fn bar1(x: Box<ShareableIterator>) { ... } // ERROR: associated type not specified
fn bar2(x: Box<ShareableIterator<Item=i32>>) { ... } // ok
fn bar3(x: Box<PrintableIterator>) { ... } // ERROR: too many traits (*)
fn bar4(x: Box<IntIterator + Sink + 'static>) { ... } // 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<Item=String>;

fn iterate<SI>(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<Item=String> {} // used in an impl Trait
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is not like the others?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

```

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<Item=Self::Response, Error=Self::Error>;
fn call(&self, req: Self::Request) -> Self::Future;
}

trait HttpService = Service<Request = http::Request, Response = http::Response, Error = http::Error>;

trait MyHttpService = HttpService<Future = MyFuture>; // assume MyFuture exists and fulfills the rules to be used in here
```

# Drawbacks
[drawbacks]: #drawbacks

- Adds another construct to the language.

- The syntax `trait TraitAlias = Trait` requires lookahead in the parser to disambiguate a trait from a trait alias.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately, this will depend on what kind of "parser generation" strategy you are using, but there isn't an inherent need for lookahead here. The key point is that the same syntax one can use on a trait, one can use on a trait alias, right up until you find either an = (trait alias) or a where/{ (trait item). The problem would be if there were overlapping but not identical parts in the prefix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so we just remove that comment from the RFC?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so


# Alternatives
[alternatives]: #alternatives

- 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<T> 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> = T: Bar, Vec<T>: Baz;`, used as `fn quux<T>(...) where Foo<T> { ... }` (i.e. direct substitution). Trait object usage is unclear.

# Unresolved questions
[unresolved]: #unresolved-questions

- Should we use `type` as the keyword instead of `trait`?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to have this listed as an alternative but not an unresolved question, in that I don't expect us to revisit this question during stabilization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider it done.


`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<i32> + f32;` to parse.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually...would expect that to parse, but fail in semantical resolution, since Rc<i32> and f32 are not traits. That is, something like type Foo = Write + Send; ought to be legal today, I would think, and has a very similar structure.

It's probably worth noting that this also interacts with the current way that we "pun" on a trait-name for the object type. If we adopted some keyword for objects, like obj Trait, then type Foo = obj (Write + Send) wouldn't make much sense for use in a where-clause (but one could have type Foo = Write + Send play some sort of "dual-role", becoming something that can be used only in an obj Foo type but not as a type itself -- this is, of course, preecisely the role that trait Foo = Write + Send would play under the current RFC)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this does parse today (but then fails at a later stage).


- 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<T: Bar> = PartialEq<T>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to type Foo<T: Bar> -- and our current handling there is not very good. I think the fix I was hoping to emply in #21903 (but I have to work out how it would work...) is to avoid issuing a warning for things that are required for the content of the trait-alias to be well-formed (in particular, bounds you need to accommodate some project).

In other words, if we applied the same intuition here, but declared that unused bounds are illegal (in types we may have to permit them because back compat):

trait Foo<T: Iterator> = PartialEq<T::Item>; // legal, `T: Iterator` implied by `T::Item`
trait Foo<T: Iterator> = PartialEq<T>; // illegal, adds `T: Iterator` from nowhere

At minimum, we should cite issue rust-lang/rust#21903

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue cited, but I don’t get your first paragraph. In what scenario should we avoid issuing a warning?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry, it's a bit confusing. What I am saying is that sometimes you need bounds, specifically when associated types are involved:

trait Foo<T: Iterator> = PartialEq<T::Item>

but other times, as in the example you gave, you are just creating "extra" bounds:

trait Foo<T: Iterator> = PartialEq<T>

I guess that for trait aliases we could interpret trait Foo<T: Iterator> = PartialEq<T> as being equivalent to trait Foo<T> = PartialEq<T> where T: Iterator, actually, so maybe most of what I wrote isn't all that relevant. Actually I think that is what we should do.

But presuming that we did not interpret it that way, and we wanted to make trait Foo<T: Iterator> = PartialEq<T> to be an error, a justification might be that T: Iterator is not "implied" by the RHS (whereas in the first example I gave, it is, since T::Item is only valid if T: Iterator holds).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type Foo<T> where T: Iterator = T; is legal syntax today. Will we support where on both sides? e,g, the lolsy trait Foo<T> where T: Iterator = where T: Iterator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a very interesting commment @Ericson2314 and I was thinking about it. I’m half editing the RFC talking about that.

```

`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<T> = where Self: PartialEq<T>, T: Bar;
```

- Do we really want hidden free type variables?

Consider the following:

```rust
trait HttpService = Service<Request = http::Request, Response = http::Response, Error = http::Error>;
```

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<F> = Service<Request = http::Request, Response = http::Response, Error = http::Error, Future = F>;
```

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.