#Applying a trait to all valid closures

9 messages · Page 1 of 1 (latest)

hard orchid
#

I am writing an ECS with the goal of running on embedded systems (the Game Boy Advance to be exact). It's a proof of concept for the moment.
Right now I'm working on component access. Currently it works but it's a very unsafe process. I'm trying to do some magic with traits to hide away that unsafeness to where it's not the developer's problem.

Something like varadic generics would be perfect, but the RFCs on that don't seem to have gotten very far yet.

My goal is to make the following syntax possible:

  old_arche_type
      .component_storage
      .access_columns(|component_vec: VecMut<T, Global>| {
        // Do stuff with that column.
      });

The most interesting part being that I can have a variable number of component_vec arguments.

#

For completeness, here's the current implementation of access_columns

    fn access_columns<A, const N: usize>(&mut self, mut accessor: A)
    where
        A: StorageAccessor<N>,
    {
        let component_ids = A::component_ids();
        let unit_component_id = ComponentId::of::<()>();
        let mut component_references: [&ComponentId; N] = [&unit_component_id; N];

        for (reference, source) in component_references.iter_mut().zip(component_ids.iter()) {
            *reference = source;
        }

        let component_vectors = self
            .components
            .get_many_mut(component_references)
            .expect("Tried to get component column that does not exist in arche type or query with duplicate items.");

        // Safety: `component_vectors` must be in the same order that `A::component_ids()` returned their IDs.
        unsafe { accessor.access(component_vectors) }
    }
#

My current attempt at a solution is to create a trait and use a macro to implement it for all the variants of functions I want to support.
Currently I'm just writing the implementation for a single column function by hand to see if the concept works.
Here's my implementation.

/// # Safety
/// The access function will be getting passed TypeErasedVec and converting it to
/// a concrete type. That is an unsafe operation. To guarentee correctness, the order
/// of component IDs returned by `component_ids()` must match the order that `access()`
/// expects them to arrive in.
unsafe trait StorageAccessor<const N: usize> {
    fn component_ids() -> [ComponentId; N];
    unsafe fn access(&mut self, columns: [&mut TypeErasedVec; N]);
}

unsafe impl<'a, P0: Any> StorageAccessor<1> for dyn FnMut(VecMut<'a, P0, Global>) {
    fn component_ids() -> [ComponentId; 1] {
        // Safety: These must be in the same order that the P arguments are provided.
        [TypeId::of::<P0>()]
    }

    unsafe fn access(&mut self, columns: [&mut TypeErasedVec; 1]) {
        // This is safe because the P argument indexed with this tells the ComponentStorage to give us the column for this P argument.
        let a = unsafe { columns[0].get_mut::<P0>() };

        // Yes, I'm using nightly feature "fn_traits" here.
        self.call_mut((a,))
    }
}
#

But for some reason when I call access_columns, the closure doesn't seem to be implementing the StorageAccessor trait, even though the signature appears to match.

error[E0277]: the trait bound `{closure@src/ecs.rs:229:29: 229:63}: StorageAccessor<_>` is not satisfied
   --> src/ecs.rs:229:29
    |
229 |             .access_columns(|component_vec: VecMut<T, Global>| {});
    |              -------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `StorageAccessor<_>` is not implemented for closure `{closure@src/ecs.rs:229:29: 229:63}`
    |              |
    |              required by a bound introduced by this call
    |
    = help: the trait `StorageAccessor<1>` is implemented for `(dyn FnMut(VecMut<'a, P0, alloc::alloc::Global>) + 'static)`
note: required by a bound in `ComponentStorage::access_columns`
   --> src/ecs.rs:73:12
    |
71  |     fn access_columns<A, const N: usize>(&mut self, mut accessor: A)
    |        -------------- required by a bound in this associated function
72  |     where
73  |         A: StorageAccessor<N>,
    |            ^^^^^^^^^^^^^^^^^^ required by this bound in `ComponentStorage::access_columns`

For more information about this error, try `rustc --explain E0277`.
hard orchid
#

I have not but reading the first page sounds a lot like what I'm trying to do.
I'll give that a read, thank you.

#

I've actually been reading the Bevy source code trying to figure this out and... they're doing something similar to what I'm doing but I don't quite understand it.

hard orchid
#

Wow thanks for that! I only had to read the first three pages to figure out what I was doing wrong.

#

I'll probably read the rest just to make sure I have the full picture.