Rust 通用值内存存储实现

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 的替代设计。谢谢你!

use*_*342 5

您可以创建一个通用特征 over Obj,指定如何Obj从存储中检索 的 arenas (Vecs) ,并将其应用到ObjectAObjectB。然后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)

操场

我建议研究竞技场板条箱,而不是实现您自己的竞技场(尽管如果您自己实现它,这并不是一个悲剧 - 重用通常是一个好主意)。