Overflow evaluating the requirement with GATs

I tried to put a where clause on generic associated type of my trait to guarantee that struct that implements Query trait could be constructed directly from the Item associated type of the Fetch associated type, but could not figure out how to make it work without compilation error.

As shown in the example, type () is definitely the same type as <<() as Query>::Fetch<'data> as Fetch<'data>>::Item, but it does not work as I expected.

#![feature(generic_associated_types)]

trait Query {
    type Fetch<'data>: Fetch<'data>
    where
        <Self::Fetch<'data> as Fetch<'data>>::Item: Into<Self>;
}

trait Fetch<'data>: Sized + Send + Sync + 'data {
    type Item: Send + Sync + 'data;
}

impl<'data> Fetch<'data> for () {
    type Item = ();
}

impl Query for () {
    type Fetch<'data> = ();
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0275]: overflow evaluating the requirement `<() as Query>::Fetch<'data> == _`
  --> src/lib.rs:18:25
   |
18 |     type Fetch<'data> = ();
   |                         ^^

For more information about this error, try `rustc --explain E0275`.
error: could not compile `playground` due to previous error

I figured out that such trait bounds are recursive and cannot be evaluated normally, because Fetch associated type has relations to its own associated type Item.

But how to rewrite such code to work as expected?

This seems to work:

trait Query: Sized {
    type Fetch<'data>: FetchAndInto<'data, Self>;
}

trait FetchAndInto<'data, T>: Fetch<'data, Item = <Self as FetchAndInto<'data, T>>::Item> {
    type Item: Send + Sync + 'data + Into<T>;
}

impl<'data, T, U> FetchAndInto<'data, T> for U
where
    U: Fetch<'data>,
    <U as Fetch<'data>>::Item: Into<T>,
{
    type Item = <U as Fetch<'data>>::Item;
}
1 Like

Such a great answer, thank you!

But I forgot to mention an additional requirement that was not in the topic: Fetch also could be implemented for types that contain some borrowed data.
For this use case, solution does not work as expected. In this playground I removed Send and Sync bounds for readability:

struct LifetimeFetch<'data, T: 'static>(core::marker::PhantomData<&'data T>);

impl<'data, T: 'static> Fetch<'data> for LifetimeFetch<'data, T> {
    type Item = &'data T;
}

impl<'a, T: 'static> Query for &'a T {
    type Fetch<'data> = LifetimeFetch<'data, T>;
}

I got this error:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'data` due to conflicting requirements
  --> src/lib.rs:40:25
   |
40 |     type Fetch<'data> = LifetimeFetch<'data, T>;
   |                         ^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
  --> src/lib.rs:39:6
   |
39 | impl<'a, T: 'static> Query for &'a T {
   |      ^^
note: ...so that the types are compatible
  --> src/lib.rs:40:25
   |
40 |     type Fetch<'data> = LifetimeFetch<'data, T>;
   |                         ^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `From<&T>`
              found `From<&'a T>`
note: but, the lifetime must be valid for the lifetime `'data` as defined here...
  --> src/lib.rs:40:16
   |
40 |     type Fetch<'data> = LifetimeFetch<'data, T>;
   |                ^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:40:25
   |
40 |     type Fetch<'data> = LifetimeFetch<'data, T>;
   |                         ^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `FetchAndInto<'data, &'a T>`
              found `FetchAndInto<'_, &'a T>`

For more information about this error, try `rustc --explain E0495`.
error: could not compile `playground` due to previous error

Well, naturally there is no Into<&'a T> for &'data T; 'a might be longer than 'data. And actually, it's not implemented at the trait level even when 'a is strictly less than 'data, only when 'a is exactly 'data.