Type Mismatch for Associated Type

I tried to compile the following code:

trait Foo {
    type Value;

    fn value() -> Self::Value
    where
        Self: Bar;
}

trait Bar: Foo {}

struct Person {}

impl<T> Foo for T {
    type Value = String;

    fn value() -> Self::Value
    where
        Self: Bar,
    {
        String::from("hello")
    }
}

impl Bar for Person {}

fn main() {
    let x = Person::value();
    println!("{}", x);
}

but I get the following error:

error[E0308]: mismatched types
  --> temp2.rs:20:9
   |
14 |     type Value = String;
   |     -------------------- expected this associated type
15 |
16 |     fn value() -> Self::Value
   |                   ----------- expected `<T as Foo>::Value` because of return type
...
20 |         String::from("hello")
   |         ^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `std::string::String`
   |
   = note: expected associated type `<T as Foo>::Value`
                       found struct `std::string::String`

For me, it's clearly that <T as Foo>::Value is std::string::String, since type Value = String;. Why the compiler cannot recognize it? Is this a compiler's bug? Or I'm doing something illogical here?

Here's some of my experiments:

  1. If I implement Foo only for Person (i.e. change the line impl<T> Foo for T to impl Foo for Person), it compiles.

  2. If I remove the restriction in value() that Self must implement Bar (i.e. remove the where Self: Bar in value() function), it compiles.

Originally, I thought the cause of this error is cycling dependency, i.e. when calling Person::value(), Person need to implement Bar (because of the where Self: Bar), which itself need to implement Foo (because of trait Bar: Foo). However, experiment 1 tells me that it's not the cause. Then I thought maybe there's something to do with generic, but experiment 2 tells me that impl<T> Foo for T works.

Background

I tried to answer this question in stackoverflow, but I failed. I'm not sure whether this is a compiler's bug or not, so I'm asking it here. If it is, then I will open an issue on github.

The Self: Bar constraint is doing something weird here, because it works without it.

I believe this behavior is expected (if unfortunate). What happens here is that the <T as Foo>::Value projection is resolved using the Self: Bar (or T: Bar) where-clause instead of the written impl. It is intentional that where-clauses are preferred over free impls, but here the where-clause does not allow the <T as Foo>::Value projection to succeed (since the where-clause does not specify the type of Value).

Interesting point. It works if you remove the : Foo from trait Bar: Foo {}.

However, if I change only the line impl<T> Foo for T to impl Foo for Person (without removing where Self::Bar) as in my experiment 1, then it also compiles. Doesn't that mean the compiler is able to resolve <T as Foo>::Value to String?

Yes, that is somewhat surprising. I'm not sure where that discrepancy comes from.

So breaking the dependency cycle (remove either where Self: Bar or : Foo) makes it compile. Use concrete type directly instead of generics (change impl<T> Foo for T to impl Foo for Person) also makes it compile. It fails only if both dependency cycle and generic exist.

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.