Maa*_*ten 3 software-design rust
我正在为对象存储开发一个 API,并且正在努力寻找一种在 Rust 中实现它的方法。这有点令人沮丧,因为我很清楚如何在 C++ 中做到这一点,所以也许这个设计从根本上不适合 Rust,但我希望这里的人们能够提供一些有用的见解。
我希望能够执行以下操作:
// 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!() }
}
Run Code Online (Sandbox Code Playgroud)
这将按如下方式使用:
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
Run Code Online (Sandbox Code Playgroud)
由于可以放入存储中的具体类型是静态已知的(只有通过方法构造它们才能在存储中create_something()),我觉得类型系统中应该有足够的信息来执行此操作。我迫切想避免的一件事是类似的事情Vec<Box<dyn Any>>,因为这会引入额外的间接性。
我意识到这在安全的 Rust 中可能是不可能的,尽管我觉得使用不安全的 Rust 应该是可能的。我的想法是,它有点类似于 Bevy ECS 实现存储组件的方式(相同类型的组件连续存储在内存中,我也想在这里看到这个属性),尽管我很难确切地理解它是如何工作的。
希望有人对如何实现这一点有想法,或者有更适合 Rust 的替代设计。谢谢你!
您可以创建一个通用特征 over Obj,指定如何Obj从存储中检索 的 arenas (Vecs) ,并将其应用到ObjectA和ObjectB。然后Store::get()使用此实现来检索值。
// Your store type:
struct Store {
a_store: Vec<ObjectA>,
b_store: Vec<ObjectB>,
}
// Trait family that specifies how to obtain slice of T's from Store
trait StoreGet<T> {
fn raw_storage(&self) -> &[T];
}
// Implementation of the trait applied to ObjectA and ObjectB on Store:
impl StoreGet<ObjectA> for Store {
fn raw_storage(&self) -> &[ObjectA] {
&self.a_store
}
}
impl StoreGet<ObjectB> for Store {
fn raw_storage(&self) -> &[ObjectB] {
&self.b_store
}
}
Run Code Online (Sandbox Code Playgroud)
完成后,将按Store如下方式实施:
impl Store {
pub fn create_object_a(&mut self) -> Id<ObjectA> {
let raw_id = self.a_store.len();
self.a_store.push(ObjectA {});
Id {
raw_id,
_marker: PhantomData,
}
}
pub fn create_object_b(&mut self) -> Id<ObjectB> {
// ... essentially the same as create_object_a ...
}
pub fn get<Obj>(&self, id: Id<Obj>) -> &Obj
where
Obj: Object,
Self: StoreGet<Obj>,
{
let slice = self.raw_storage();
&slice[id.raw_id]
}
}
Run Code Online (Sandbox Code Playgroud)
我建议研究竞技场板条箱,而不是实现您自己的竞技场(尽管如果您自己实现它,这并不是一个悲剧 - 重用通常是一个好主意)。