GATs: limitation, bug, or misunderstanding on my part?

I'm testing out GATs, and I'm hitting a weird error message that I can't make sense of, and I'm wondering whether I'm hitting a known limitation, a bug in the implementation, or if I am just misunderstanding something.

My reduced test case looks like the following:

#![feature(generic_associated_types)]
pub trait Foo {
    type Type<'a>;
}
pub trait Bar {}
pub struct Qux<T>(T);
pub fn bar(_: impl Bar) {}

impl<T: Foo> Bar for Qux<T> where for<'a> <T as Foo>::Type<'a>: Bar {}

impl Foo for String {
    type Type<'a> = Self;
}
impl Bar for String {}

pub fn foo() {
    bar(Qux("foo".to_string()));
}

This fails to compile with:

error[E0277]: the trait bound `for<'a> <String as Foo>::Type<'a>: Bar` is not satisfied
  --> src/lib.rs:17:5
   |
7  | pub fn bar(_: impl Bar) {}
   |                    --- required by this bound in `bar`
...
17 |     bar(Qux("foo".to_string()));
   |     ^^^ the trait `for<'a> Bar` is not implemented for `<String as Foo>::Type<'a>`
   |
   = note: required because of the requirements on the impl of `Bar` for `Qux<String>`

The simpler version of the code, without GATs, compiles just fine, which is what confuses me:

pub trait Foo {
    type Type;
}
pub trait Bar {}
pub struct Qux<T>(T);
pub fn bar(_: impl Bar) {}

impl<T: Foo> Bar for Qux<T> where <T as Foo>::Type: Bar {}

impl Foo for String {
    type Type = Self;
}
impl Bar for String {}

pub fn foo() {
    bar(Qux("foo".to_string()));
}

This compiles (but honestly I don't grok the intention from the example and am unsure if that's what you need).

A more realistic test case looks like:

#![feature(generic_associated_types)]
use std::cmp::PartialEq;
pub trait Foo {
    type Type<'a>;
    fn foo<'a>(&'a self) -> Self::Type<'a>;
}
pub struct Qux<T>(T);

impl<T: Foo> PartialEq for Qux<T> where for<'a> <T as Foo>::Type<'a>: PartialEq
{
    fn eq(&self, other: &Self) -> bool {
        self.0.foo().eq(&other.0.foo())
    }
}

impl Foo for String {
    type Type<'a> = &'a str;
    fn foo<'a>(&'a self) -> &'a str {
        &self
    }
}

pub fn foo() {
    let q = Qux("foo".to_string());
    q.eq(&q);
}

And fails with:

error[E0599]: the method `eq` exists for struct `Qux<String>`, but its trait bounds were not satisfied
  --> src/lib.rs:25:7
   |
7  | pub struct Qux<T>(T);
   | ---------------------
   | |
   | method `eq` not found for this
   | doesn't satisfy `Qux<String>: Iterator`
   | doesn't satisfy `Qux<String>: PartialEq`
...
25 |     q.eq(&q);
   |       ^^ method cannot be called on `Qux<String>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `<String as Foo>::Type<'a>: PartialEq`
           which is required by `Qux<String>: PartialEq`
           `Qux<String>: Iterator`
           which is required by `&mut Qux<String>: Iterator`

Replacing q.eq(&q); with PartialEq::eq(&q, &q); gives a different error:

error[E0277]: can't compare `<String as Foo>::Type<'a>` with `<String as Foo>::Type<'a>`
  --> src/lib.rs:25:5
   |
25 |     PartialEq::eq(&q, &q);
   |     ^^^^^^^^^^^^^ no implementation for `<String as Foo>::Type<'a> == <String as Foo>::Type<'a>`
   |
   = help: the trait `for<'a> PartialEq` is not implemented for `<String as Foo>::Type<'a>`
   = note: required because of the requirements on the impl of `PartialEq` for `Qux<String>`
   = note: required by `std::cmp::PartialEq::eq`

This could well be a HRTB problem rather than GAT, because the following fails to compile with a similar error:

use std::cmp::PartialEq;
pub trait Foo<'a> {
    type Type;
    fn foo(&'a self) -> Self::Type;
}
pub struct Qux<T>(T);

impl<T> PartialEq for Qux<T> where for<'a> T: Foo<'a>, for<'a> <T as Foo<'a>>::Type: PartialEq
{
    fn eq(&self, other: &Self) -> bool {
        self.0.foo().eq(&other.0.foo())
    }
}

impl<'a> Foo<'a> for String {
    type Type = &'a str;
    fn foo(&'a self) -> &'a str {
        &self
    }
}

pub fn foo() {
    let q = Qux("foo".to_string());
    PartialEq::eq(&q, &q);
}

The error:

error[E0277]: can't compare `<String as Foo<'a>>::Type` with `<String as Foo<'a>>::Type`
  --> src/lib.rs:24:5
   |
24 |     PartialEq::eq(&q, &q);
   |     ^^^^^^^^^^^^^ no implementation for `<String as Foo<'a>>::Type == <String as Foo<'a>>::Type`
   |
   = help: the trait `for<'a> PartialEq` is not implemented for `<String as Foo<'a>>::Type`
   = note: required because of the requirements on the impl of `PartialEq` for `Qux<String>`
   = note: required by `std::cmp::PartialEq::eq`

And adding an intermediate trait works around it, but that doesn't sound great:

use std::cmp::PartialEq;
pub trait Foo<'a> {
    type Type;
    fn foo(&'a self) -> Self::Type;
}
pub trait Bar<'a> {
    type Type: PartialEq;
    fn bar(&'a self) -> Self::Type;
}
impl<'a, T: Foo<'a>> Bar<'a> for T where T::Type: PartialEq {
    type Type = T::Type;
    fn bar(&'a self) -> Self::Type { self.foo() }
}

pub struct Qux<T>(T);

impl<T> PartialEq for Qux<T> where for<'a> T: Bar<'a>
{
    fn eq(&self, other: &Self) -> bool {
        self.0.bar().eq(&other.0.bar())
    }
}

impl<'a> Foo<'a> for String {
    type Type = &'a str;
    fn foo(&'a self) -> &'a str {
        &self
    }
}

pub fn foo() {
    let q = Qux("foo".to_string());
    PartialEq::eq(&q, &q);
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.