genericsrustspecs

Rust Specs Crate Generic Component in System


I am trying to implement a system to use a generic component, but I keep having issues with join method when I try to mutate the variable. Here is a simplified version of the code I have:

use specs::{Component, prelude::*};


#[derive(Component, Debug, Default)]
#[storage(NullStorage)]
pub struct FooMarker;



#[derive(Component, Debug, Default)]
#[storage(VecStorage)]
pub struct Foo(pub f64);



pub trait Variable: Component + std::fmt::Debug + Default + Send + Sync + 'static {
    fn get_value(&self) -> f64;
    fn set_value(&mut self, value: f64);
}


impl Variable for Foo {
    fn get_value(&self) -> f64 {
        self.0
    }
    fn set_value(&mut self, value: f64) {
        self.0 = value;
    }
}


#[derive(Default)]
pub struct ChangeVariableSystem<T: Default=Foo> {
    _phantom_variable: std::marker::PhantomData<T>,
}


impl <'a, T> System<'a> for ChangeVariableSystem<T> 
where
    T: Component + Variable + Default + Send + Sync + 'static,
{
    type SystemData = (
        ReadStorage<'a, FooMarker>,
        WriteStorage<'a, T>,
    );

    fn run(&mut self, data: Self::SystemData) {
        let (
            markers,
            mut variables,
        ) = data;

        (&variables, &markers).join()
            .for_each(|(variable, _)| {
                variable.get_value();
                println!("{:?}", variable);
            });

        (&mut variables, &markers).join()
            .for_each(|(variable, _)| {
                variable.set_value(1.0);
                println!("{:?}", variable);
            });
    }
}


fn main() {
    let mut world = World::new();
    
    
    world.register::<Foo>();
    world.register::<FooMarker>();
    
    for _ in 0..10 {
        world.create_entity()
            .with(Foo(0.0))
            .with(FooMarker)
            .build();
    }
    
    let mut dispatcher = DispatcherBuilder::new()
        .with(
            ChangeVariableSystem::<Foo>::default(), 
            "change_variable_system", 
            &[],
        ).build();    
}

In the previous code, I had no issues in the first join because the variable was not mutated, but in the second one, I got the following issue:

error[E0599]: the method `join` exists for tuple `(&mut Storage<'_, T, FetchMut<'_, MaskedStorage<T>>>, &Storage<'_, FooMarker, Fetch<'_, 
MaskedStorage<FooMarker>>>)`, but its trait bounds were not satisfied
  --> project\examples\generics.rs:60:36
   |
60 |         (&mut variables, &markers).join()
   |                                    ^^^^ method cannot be called due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&mut Storage<'_, T, FetchMut<'_, MaskedStorage<T>>>: specs::Join`
           which is required by `(&mut Storage<'_, T, FetchMut<'_, MaskedStorage<T>>>, &Storage<'_, FooMarker, Fetch<'_, MaskedStorage<FooMarker>>>): specs::Join`
           `<T as specs::Component>::Storage: SharedGetMutStorage<T>`
           which is required by `(&mut Storage<'_, T, FetchMut<'_, MaskedStorage<T>>>, &Storage<'_, FooMarker, Fetch<'_, MaskedStorage<FooMarker>>>): specs::Join`

For more information about this error, try `rustc --explain E0599`.

I can't figure out how to solve this. Shouldn't the Component trait binding be enough to solve the issue? Can you help me?

I just tried the same thing using specs version 0.19 and it worked. Is this a bug of 0.20 version?

Thanks!


Solution

  • v0.20.0 has indeed changed that (added a trait bound). You now need to add where T::Storage: SharedGetMutStorage<T> in the bounds.

    You also need to add let variable = variable.access_mut(); at the beginning of the second closure, to access the value.