Compilation error using higher kinded types trick

I'm attempting to adapt the higher kinded types trick in the linked blog post to my scenario. Essentially, I have a trait with a method that needs to vary the return value based on the implementing concrete type.

I'm able to recreate the code samples in the link on the playground and they work as intended. However, when I modify the example to this minimal repro:

struct Foo<T> {
    _phantom: std::marker::PhantomData<T>
}

trait Hkt<B> {
    type R;
}

impl<A, B> Hkt<B> for Foo<A> {
    type R = u64;
}

trait Horse {
    fn horse<B>(&self) -> <Self as Hkt<B>>::R where Self: Hkt<B>;
}

impl<A> Horse for Foo<A> {
    fn horse<B>(&self) -> <Self as Hkt<B>>::R where Self: Hkt<B> {
        16u64
    }
}

I get the error:

error[E0308]: mismatched types
  --> src/lib.rs:20:9
   |
18 |     fn horse<B>(&self) -> <Self as Hkt<B>>::R where Self: Hkt<B> {
   |                           ------------------- expected `<Foo<A> as Hkt<B>>::R` because of return type
20 |         16u64
   |         ^^^^^ expected associated type, found `u64`
   |
   = note: expected associated type `<Foo<A> as Hkt<B>>::R`
                         found type `u64`
   = help: consider constraining the associated type `<Foo<A> as Hkt<B>>::R` to `u64` or calling a method that returns `<Foo<A> as Hkt<B>>::R`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (lib) due to 1 previous error

I'm failing to see what I'm doing wrong. For all B, A, all Foo<A> impls Hkt<B>, so horse() should resolve to a u64. What gives?

Oh, I see it's complaining because I didn't specify the associated type in the Hkt bound on horse's return value. If I do:

    fn horse<B>(&self) -> <Self as Hkt<B>>::R where Self: Hkt<B, R=u64>;

on both the trait and the impl, it now compiles, but unfortunately, isn't what I want. I want to allow the impl to vary the return value according to its Hkt impl.

Did a bit of poking around and it turns out this is a good use case for GATs :slight_smile:

This does exactly what I want:

#[derive(Debug)]
struct Foo<T> {
    _phantom: std::marker::PhantomData<T>
}

trait Horse {
    type Ret<B>;

    fn horse<B>(&self) -> Self::Ret<B>;
}

impl<A> Horse for Foo<A> {
    type Ret<B> = Foo<B>;

    fn horse<B>(&self) -> Self::Ret<B> {
        Foo { _phantom: std::marker::PhantomData }
    }
}

fn main() {
    dbg!(Foo::<u64> { _phantom: std::marker::PhantomData }.horse::<u16>());
}