RFC: Associated type bounds of form MyTrait<AssociatedType: Bounds> by Centril · Pull Request #2289 · rust-lang/rfcs (original) (raw)

@burdges It depends on what you wish to communicate.

Consider: where X: Family<Assoc<T>: Bound>. I would translate this to the bounds X: Family and X::Assoc<T>: Bound.

Consider: where X: for<T> Family<Assoc<T>: Bound>. I would translate that to the bounds: X: Family and for<T> X::Assoc<T>: Bound.

These two bounds are clearly different (even if the latter builds upon the combination of for<T> and Family<Assoc<T>: Bound>). The former talks about a specific T applied to Assoc satisfying Bound while the latter says that for any T you pick, T applied to Assoc, Assoc<T> satisfies Bound.

At this point; I think it is too early to introduce Assoc<T>: Bound given that GATs are not even in the nightly compiler. Once we gain more experience we can see if this extension is worthwhile;

Regarding Family<Assoc: Bound> as sugar for for<T> Family<Assoc<T>: Bound>, I think that this would be surprising if we don't have HKTs. If however we do introduce HKTs, this would translate to Functor (AssocFunctor MyFamily) (as a concrete example) in Haskell. Then I do think it makes sense. But this addition is premature.

EDIT: Actually, no... that was wrong! I don't see Family<Assoc: Bound>, where Assoc is higher kinded, meaning for<T> Family<Assoc<T>: Bound> because in the case of HKTs, it is the functor that has the bound, and not the functor applied to some type argument.

Regarding Trait<Assoc = impl Bounds>, the meaning depends on if it is in argument position or return position. If it is in return position, that is: -> impl T<A = impl B>, then the type of A is existential as seen in this example:

fn foo() -> impl Iterator<Item = impl Clone> { ::std::iter::empty::() }

Therefore, the callee picks the type of Item.

If however you have impl T<A = impl B> in argument position, you get:

fn bar(mut iter: impl Iterator<Item = impl Clone>) { let x: Option<()> = iter.next(); }

However; this snippet does not compile because the caller, and not the callee, gets to pick the type, and so we get:

10 | let x: Option<()> = iter.next(); | ^^^^^^^^^^^ expected (), found type parameter