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);
}