Relax "unconstrained type parameter" requirements?

Currently, Rust rejects this code:

struct Foo<A, B> {
    a: A,
    b: B,
}

impl<A, B, T: AsRef<Foo<A, B>>> AsRef<A> for T {
    fn as_ref(&self) -> &A {
        &self.as_ref().a
    }
}
error[E0207]: the type parameter `B` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:6:9
  |
6 | impl<A, B, T: AsRef<Foo<A, B>>> AsRef<A> for T {
  |         ^ unconstrained type parameter

Error E0207 exists for good reason. It causes code like the following to be (rightly) rejected (credit to this issue for the code snippet):

trait Maker {
    type Item;

    fn make(&mut self) -> Self::Item;
}

struct Foo<T> {
    a: T,
}

struct Bar;

impl<T: Default> Maker for Bar {
    type Item = Foo<T>;

    fn make(&mut self) -> Foo<T> {
        Foo { a: T::default() }
    }
}

The reason that this code should be rejected is that it's an explicit design goal to have the value of an associate type be uniquely determined by the trait, the trait parameters, and the type. In this code snippet, even knowing all of Maker and Bar (Maker takes no type arguments, so those aren't relevant here), we don't know the value of <Bar as Maker>::Item.

However, this reasoning doesn't seem to apply in cases in which the trait has no associated types such as the example I gave at the top. Is there a reason we couldn't relax E0207 to permit cases like this?

Take this type:


struct T1;
struct T2;

struct S<A>(Foo<A,T1>, Foo<A,T2>);

impl<A> AsRef<Foo<A,T1>> for S<A> {
    fn as_ref(&self) -> &Foo<A,T1> { &self.0 }
}
impl<A> AsRef<Foo<A,T2>> for S<A> {
    fn as_ref(&self) -> &Foo<A,T2> { &self.1 }
}

Now your implementation of S<A>: AsRef<A> could refer to the a: A inside the first or the second field of S<A>.

5 Likes