Making an ECS: Accessing a struct's field through a function's generic type

Hi, I am working on creating my own ECS, and I've been looking at entt for guidance. There, in order to attribute a component to an entity you simply do:

registry.emplace<position>(entity, i * 1.f, i * 1.f);`

position is a type, a struct with two fields. Since I'm not particularly experienced or creative, I figured I'd copy this API, but I'm not sure how to go about it in rust. So far I have developed a basic sparse set, which is what holds entity IDs and components:

pub struct SparseSet<T>
{
    dense: Vec<EntID>,
    sparse: Vec<EntID>,
    data: Vec<T>
}

My question is, how can I easily instance these sparse sets in the registry, and access them through a generic function? Is it possible? I am not an expert, so I figured I could go with a specialized implementation if it makes things easier, in which you explicitly list the components (types) in the ECS.

My first thought was creating a macro that adds a SparseSet of the provided type to the registry struct, and implements the generic function emplace for that type, returning a reference to the respective field. My experience with macros is limited, so I would like feedback on whether this is likely to be a good solution and if I should pursue it.

If you have a suggestion for an alternative API I would also be glad to hear it. Thank you for the help!

I found a solution using a HashMap and TypeId, as well as type erasure through Box

pub struct Registry{
    sets: HashMap<TypeId, Box<dyn Any>>,
}

Creating a new set is pretty easy

    /// Creates a new sparse set of type [`T`]
    fn create_set<T: Component>(&mut self) {
        self.sets.insert(TypeId::of::<T>(), Box::new(SparseSet::<T>::new()));
    }

Getting it with the proper type is also simple, just requires a downcast using the generic type:

    /// Returns an [`Option`] containing a reference to [`T`]'s respective set
    /// 
    /// Returns [`None`] if the set does not exist
    fn get_set<T: Component>(&mut self) -> Option<&SparseSet<T>> {
        self.sets.get_mut(&TypeId::of::<T>())
            .map(|boxed_set| boxed_set.downcast_ref::<SparseSet<T>>().expect("Downcast should work"))
    }

If my code is not ideal or I made any mistake naming any features/concepts, feel free to correct me, I'm still getting started.