Generics, Casting, Abstract class analog

Hi, on my adventure to learn rust i've gotten stuck on finding a solution to the following.
Im trying to store a map of vectors containing an arbitrary component type.
I Manage fine adding / removing etc components when i know the type i am wanting to manipulate, however i am getting stuck on how to have a function that without knowing the specific type to remove all components with the associated owner id.
In c++ / java i would just extend from an abstract class

struct ComponentHandler<T> {
    components: Vec<T>,
    owner: Vec<u32>
}
#[derive(Debug)]
pub struct ComponentManager {
    map: HashMap<TypeId, Box<dyn Any>>
}
impl<T> ComponentHandler<T> {
    pub fn new()->Self {}
    pub fn remove_all_components_from(&mut self, eid:u32) {}
    pub fn get_mut_component(&mut self, eid:u32) -> Option<&mut T> {}
    pub fn remove_component_from(&mut self, eid: u32) {}
    pub fn add_component(&mut self, component: T, id: u32) {}
}
impl ComponentManager {
    pub fn new() ->Self {}
    pub fn remove_all_components_from(&mut self, id:u32) {
        for comp_list in &mut self.map {
            let list: Option<&mut ComponentHandler> = comp_list.1.downcast_mut::<ComponentHandler>();
            if list.is_some() {
                list.unwrap().remove_all_components_from(id);
            }
        }
    }
    pub fn add_component<T:'static>(&mut self, component:T, id: u32) {}
    pub fn remove_component_from<T: 'static>(&mut self, eid:u32) {}
    pub fn get_mut_component<T:'static>(&mut self, eid: u32) ->Option<&mut T> {}
}

Rust doesn't have higher-kinded types, so there is no ComponentHandler type that encompasses ComponentHandler<T> for all possible T. The only subtyping Rust has centers around lifetimes. There's also no runtime typing to let you go from TypeId to a type, just the other way around.

So, you would have to know the possible T so you could attempt to downcast to...

downcast_mut::<ComponentHandler<T1>>
downcast_mut::<ComponentHandler<T2>>
downcast_mut::<ComponentHandler<T3>>
downcast_mut::<ComponentHandler<T4>>
...

...which is probably a sign the design needs a rethink.

(Defaulting to type erasure and then frequently downcasting isn't a common Rust pattern.)


If everything you need to do on a ComponentHandler<T> without knowing T is representable by a trait, perhaps you want to type erase using a custom trait rather than dyn Any. Until we get supertrait upcasting, you'd have to roll your own upcasting to dyn Any or roll your own downcasting, if you wanted to keep the changes from your OP minimal (keep the downcasting for get_mut_component and the like).

Here's a quick, mostly unchecked example.

Or maybe you'd be better off with a full blown ECS or such.

2 Likes

At the end of the day i would most probably be better off using a full ecs, but the choice of the project was to push my understanding of the language so i will look into the linked resources
Thanks a heap :smiley:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.