Implementing a trait for types implementing a generic trait whose type parameter is any type that implements another trait


#1
trait Collection<T> {}
trait Nice {}
trait Blue {}

// this works
impl<T> Nice for T
where
    T: Collection<usize>
{}

// this does not
impl<T, U> Nice for T
where
    T: Collection<U>,
    U: Blue,
{}
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
  --> src/lib.rs:10:9
   |
10 | impl<T, U> Nice for T
   |         ^ unconstrained type parameter

I’m not making this up. I actually need it, and I don’t understand why it doesn’t work. playground


#2

Imagine you have:

struct Foo;
struct Bar;

impl Blue for Foo {}
impl Blue for Bar {}

struct Coll;

impl Collection<Foo> for Coll {}
impl Collection<Bar> for Coll {}

The blanket impl you’re attempting doesn’t uniquely identify which of those Coll it should pick for T.

The usual answer here is to switch to associated types:

trait Collection {
   type Item;
}

impl<T, U> Nice for T
where
    T: Collection<Item = U>,
    U: Blue,
{}

#3

The compiler can’t even decide this case, because that Collection<U> could be implemented in some future crate! For instance, somewhere there could be something like:

struct X;
impl Blue for X {}
impl Collection<X> for i32 {}

… and then your blanket impl would cover Nice for i32 too, everywhere.

The compiler needs a consistent answer whether i32 implements Nice, and it can’t wait for all possible crates to decide this. But if it were constrained like Nice<U>, then we only ever need to resolve implementations for a U that we already know about.


#4

I can’t switch to associated types because in my case it’s a trait from the standard library, std::ops::RangeBounds<T>