Higher ranked trait bounds and function parameters

Sep 13 2020

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}`

Je pense que le compilateur voit la durée 'rde vie déduite dans fn hmm<'r>(&'r i32)comme étant différente de la durée 'ade vie définitive dans type Fetch: for<'a> Fetch<'a>. Je ne vois pas l'astuce que Bevy utilise pour réaliser la même chose.

Réponses

1 cart Sep 17 2020 at 02:51

Vous êtes vraiment très proche!

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

C'est ... très frustrant car cela IntoForEachSystem::system(hmm)devrait être équivalent à hmm.system()en ce qui me concerne. C'est peut-être un bogue dans le compilateur Rust?