Search code examples
rusthigher-rank-typesbevy

Higher ranked trait bounds and function parameters


I'm trying to understand the implementation of Bevy's IntoForEachSystem trait and the way it interacts with the underlying Hecs Query and Fetch traits. Hecs has query types (the thing you request in a call to query::<T>) and item types (the thing returned by the query). The idea is that IntoForEachSystem is implemented for closures whose query type matches the query's item type, and fn f(&i32) works because an &i32 query returns an &i32 item.

I think I extracted the relevant parts of the design in this snippet, but I can't make it type check:

// Hecs Query trait
trait Query {
    type Fetch: for<'a> Fetch<'a>;
}

// Hecs Query trait implementation for read-only references
impl<'a, T> Query for &'a T
where
    T: 'static,
{
    type Fetch = FetchRead<T>;
}

// Hecs Fetch trait
trait Fetch<'a>: Sized {
    type Item;
}

// Hecs Fetch trait implementation for read-only references
struct FetchRead<T>(std::marker::PhantomData<T>);

impl<'a, T> Fetch<'a> for FetchRead<T>
where
    T: 'static,
{
    type Item = &'a T;
}

// Bevy IntoForEachSystem trait, simplified
trait IntoForEachSystem<R> {
    fn system(self);
}

// Bevy IntoForEachSystem trait implementation for functions of one argument
impl<F, R> IntoForEachSystem<R> for F
where
    F: Fn(R),
    F: Fn(<<R as Query>::Fetch as Fetch>::Item),
    R: Query,
{
    fn system(self) {
        println!("hello");
    }
}

fn hmm(_x: &i32) {
    todo!()
}

fn main() {
    IntoForEachSystem::system(hmm)
}

Errors:

error[E0631]: type mismatch in function arguments
   |
31 |     fn system(self);
   |     ---------------- required by `IntoForEachSystem::system`
...
46 | fn hmm(_x: &i32) {
   | ---------------- found signature of `for<'r> fn(&'r i32) -> _`
...
51 |     IntoForEachSystem::system(hmm)
   |                               ^^^ expected signature of `for<'r> fn(<FetchRead<i32> as Fetch<'r>>::Item) -> _`
   |
   = note: required because of the requirements on the impl of `IntoForEachSystem<&i32>` for `for<'r> fn(&'r i32) {hmm}`

I think the compiler is seeing the inferred lifetime 'r in fn hmm<'r>(&'r i32) as being different from the forall lifetime 'a in type Fetch: for<'a> Fetch<'a>. I don't see the trick that Bevy is using to achieve the same thing.


Solution

  • You're actually super close!

    fn main() {
        hmm.system();
    }
    

    This is ... very frustrating because IntoForEachSystem::system(hmm) should be equivalent to hmm.system() as far as I'm concerned. Maybe it's a bug in the Rust compiler?