Return a struct based upon generic argument

Hello, rust community!
I am developing an ECS based application (mostly as a way to learn more advanced topics of Rust by building a larger scale project), but I have encountered an issue that I am not able to resolve.
Take this (it is a simplification of the actual implementation)

pub struct TComponents{
    field1:HashMap<IndexType,components::Field1>, field2:HashMap<IndexType, components::Field2>,
}
impl TComponents{
    fn get<T:Component>(&self)->&HashMap<IndexType,T>{
        match T::get_type(){
            components::ComponentType::Fields=>{&self.fields}
        }
    }
}

The idea is to be able to return the corresponding type given a generic argument that implements a certain trait. But this does not work! Anyone have an idea as to why, and how I might achieve similar functionality?

There is no way to make this signature work. Returning a reference to a HashMap, with the same lifetime as the borrow of self, implies that such a HashMap must be stored somewhere in memory, accessible from self. Your signature claims to be able to return such a reference to HashMap<IndexType, T> for every T that implements Component, but this is certainly not possible—if I, the user of your library, implement Component for my own type Foo, there is no way your TComponents has a HashMap<IndexType, Foo> stashed away somewhere.

There are a bunch of existing ECS libraries written in Rust (Specs, Legion, Shipyard, Bevy); maybe looking at what they do would help you find a good approach for your own project?

Not exactly. It claims that you either return such Component or die trying!

Should work if you would call panic! in case of failure.

But I would, probably, return Option<&HashMap<IndexType, T>> instead. To give caller a chance to do something with such failure.

I managed to make this work, but I had to use an unsafe block. This is not actually a library. The trait implementation is generated for any struct via a reasonably simple proc macro.

             pub fn get<T:crate::ecs::Component>(&self)->&HashMap<IndexType,T>{
                let m:&HashMap<IndexType,T> =unsafe{match T::get_type(){
                  #(ecs::components::ComponentType::#n=>std::mem::transmute(&self.#sn),)*
                }
            };
            m
            }

This seems to work.

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.