Description
I am not sure if this is a bug or a documentation issue.
The Rust Reference states:
- In trait declarations as supertraits:
trait Circle: Shape {}
is equivalent totrait Circle where Self: Shape {}
- In trait declarations as bounds on associated types:
trait A { type B: Copy; }
is equivalent totrait A where Self::B: Copy { type B; }
This matches the associated type bounds RFC:
- The surface syntax
T: Trait<AssociatedType: Bounds>
should desugar to a pair of bounds:T: Trait
and<T as Trait>::AssociatedType: Bounds
.- The new syntax does not introduce any new semantics.
The stabilization PR says almost the same thing, but with a subtle difference:
- […]
where T: Trait<Assoc: Bound>
is equivalent towhere T: Trait, <T as Trait>::Assoc: Bound
.- Supertraits - Similar to above,
trait CopyIterator: Iterator<Item: Copy> {}
. This is almost equivalent to breaking up the bound into two (or more)where
clauses; however, the bound on the associated item is implied whenever the trait is used. See Should associated type bounds on supertraits be implied? #112573/Make associated type bounds in supertrait position implied #112629.- Associated type item bounds - This allows constraining the nested rigid projections that are associated with a trait's associated types. e.g.
trait Trait { type Assoc: Trait2<Assoc2: Copy>; }
.
(Emphasis mine.)
However, the PR linked in that second bullet, Make associated type bounds in supertrait position implied, implies that this where
clause implies the same bounds as the B<Assoc: C>
syntax:
trait A: B<Assoc: C> {}
should be able to imply bothSelf: B
and<Self as B>::Assoc: C
.
For normal associated types, this is definitely the case, but for bounds on associated types of supertraits, and bounds on associated types of associated types, these two forms are demonstrably not equivalent:
This compiles:
// A trait which has bounds on a nested associated type,
// using shorthand associated type bounds syntax.
// Associated type bounds ARE implied elsewhere.
pub trait NestedSugar
{
// Sufficient.
type Output: Iterator<Item: Clone>;
fn make() -> Self::Output;
}
pub fn nested_sugar<T>() -> <T::Output as Iterator>::Item
where
T: NestedSugar,
// `No <T::Output as Iterator>::Item: Clone` required.
{
T::make().next().unwrap().clone()
}
And this compiles:
// Supertrait with bounds on the associated type of the subtrait,
// using shorthand associated type bounds syntax.
// Supertrait associated type bounds ARE implied elsewhere.
pub trait SuperSugar
where
Self: Iterator<Item: PartialEq>,
{
fn super_next_sugar(&self) -> <Self as Iterator>::Item;
}
pub fn take_sugar<I>(iter: I) -> bool
where
I: SuperSugar,
// No `<I as Iterator>::Item: PartialEq` required.
{
let first = iter.super_next_sugar();
let second = iter.super_next_sugar();
first == second
}
But this does not compile:
// A trait which has bounds on a nested associated type,
// using a where clause.
// The associated type bounds are NOT implied elsewhere.
pub trait NestedWhere
where
Self::Output: Iterator,
// Not sufficient.
<Self::Output as Iterator>::Item: Clone,
{
type Output;
// GAT-ish syntax is also not sufficient:
//type Output: Iterator where <Self::Output as Iterator>::Item: Clone;
fn make() -> Self::Output;
}
pub fn nested_where<T>() -> <T::Output as Iterator>::Item
where
T: NestedWhere,
// Required. Does not compile without this line:
//<T::Output as Iterator>::Item: Clone,
// error[E0277]: the trait bound `<<T as NestedWhere>::Output as Iterator>::Item: Clone` is not satisfied
{
T::make().next().unwrap().clone()
}
Nor does this:
// Supertrait with bounds on the associated type of the subtrait,
// using a where clause.
// Associated type bounds are NOT implied elsewhere.
pub trait SuperWhere
where
Self: Iterator,
// Not sufficient.
<Self as Iterator>::Item: PartialEq,
{
fn super_next_where(&self) -> <Self as Iterator>::Item;
}
pub fn take_where<I>(iter: I) -> bool
where
I: SuperWhere,
// Required. Does not compile without this line:
//<I as Iterator>::Item: PartialEq,
// error[E0277]: can't compare `<I as Iterator>::Item` with `<I as Iterator>::Item`
// help: the trait `PartialEq` is not implemented for `<I as Iterator>::Item`
{
let first = iter.super_next_where();
let second = iter.super_next_where();
first == second
}
Playground
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=faec69dd74703073da0a6183cceb9afd
See also
- rust-lang/rust#58231: Trait bounds on associated type causes confusing compilation error
- rust-lang/rust#57905: Compiler unable to apply trait bound
- rust-lang/rust#20671: where clauses are only elaborated for supertraits, and not other things
- rust-lang/rust#103387:
where
ontrait
declaration should be provided when the trait is an input bound, but is instead a requirement
All of those issues talk about type bounds not being implied in one case or another, but as far as I can tell there is no issue about the discrepancy of implied bounds between these two syntaxes. And again I am not sure if this is a bug or a documentation (and possibly diagnostics) issue.