I am working on an API for an object store, and I am struggling to figure out a way to implement it in Rust. It is a bit frustrating, since I have a good idea of how this could be done in C++, so maybe the design is fundamentally not suitable for Rust, but I hope people here can provide some useful insights.
I would like to be able to do the following:
// This is clone & copy
struct Id<Identifiable> {
// What I think would be needed to get this entire system
// to work, but it is not fixed like this so alternate
// ideas are welcome.
raw_id: usize,
_marker: PhantomData<Identifiable>,
}
struct Store {
// ... don't know how to implement this
}
trait Object {
// ... some behavior
}
struct ObjectA {}
impl Object for ObjectA {}
struct ObjectB {}
impl Object for ObjectB {}
impl Store {
pub fn create_object_a(&mut self) -> Id<ObjectA> { todo!() }
pub fn create_object_b(&mut self) -> Id<ObjectB> { todo!() }
pub fn get<'a, Obj: Object>(&self, id: Id<Obj>) -> &'a Obj { todo!() }
}
This would be used as follows:
let store = Store::new();
let handle_a = store.create_object_a();
let handle_b = store.create_object_b();
let obj_a = store.get(handle_a); // obj_a is of type &ObjectA
let obj_b = store.get(handle_b); // obj_b is of type &ObjectB
Since the concrete types that can be put into the store are known statically (they can only be in the store if they are constructed through a create_something() method), I feel like there should be enough information in the type system to be able to do this. One thing I desperately want to avoid is something like Vec<Box<dyn Any>>, because that introduces extra indirection.
I realize that this is likely not possible in safe Rust, although I feel like it should be possible using unsafe Rust. My thought is that it is somewhat similar to how the Bevy ECS implementation stores components (components of the same type are stored contiguously in memory, a property I would like to see here as well), although I am struggling to understand exactly how that works.
Hopefully someone has ideas on how to implement this, or has an alternate design which would suite Rust better. Thank you!