Rust 通用值内存存储实现

标签 rust software-design

我正在为一个对象存储开发一个 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!() }
}

这将按如下方式使用:

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

由于可以放入商店的具体类型是静态已知的(如果它们是通过 create_something() 方法构建的,它们只能在商店中),我觉得类型系统中应该有足够的信息来能够做到这一点。我非常想避免的一件事是 Vec<Box<dyn Any>>。 ,因为这引入了额外的间接性。

我意识到这在安全的 Rust 中可能是不可能的,尽管我觉得使用不安全的 Rust 应该是可能的。我的想法是它有点类似于 Bevy ECS 实现存储组件的方式(相同类型的组件连续存储在内存中,我也希望在这里看到这个属性),尽管我正在努力理解它是如何工作的.

希望有人对如何实现这个有想法,或者有一个更适合 Rust 的替代设计。谢谢!

最佳答案

您可以在 Obj 上创建一个通用特征,指定如何从存储中检索 Obj 的竞技场 (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
    }
}

有了它,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]
    }
}

Playground

我建议查看 an arena crate而不是实现自己的竞技场(虽然如果你自己实现它并不是悲剧 - 重用通常是个好主意)。

关于Rust 通用值内存存储实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72241916/

相关文章:

rust - 为什么多行字符串在开始时会跳过预期的空格?

rust - 如何为我不拥有的类型的固有方法创建特征?

string - 如何编写一个同时接受拥有和非拥有的字符串集合的函数?

rust - 为什么 poll() 内的延迟 future 在我的自定义流类型中不起作用?

java - 使用“持续时间”、“期间”或“年月”作为类字段类型是否合适?

boolean - 预期类型 `bool` ,发现类型 `&bool`

java - 向 Java Bean 添加业务逻辑的良好设计

Java 设计替代 if-else with instanceof

wireshark - 像 Wireshark 这样的应用程序如何工作?

c++ - 如何避免这种嵌套的 if 语句?