我正在为一个对象存储开发一个API,我正在努力找到一种在Rust中实现它的方法.这有点令人沮丧,因为我对如何在C++中实现这一点有一个很好的 idea ,所以可能这个设计从根本上不适合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 应该是可能的.我的 idea 是,它在某种程度上类似于Bevy ECS实现存储组件的方式(相同类型的组件连续存储在内存中,我也想在这里看到这一特性),尽管我很难确切地理解它是如何工作的.

希望有人对如何实现这一点有 idea ,或者有一个更好的替代设计.非常感谢.

推荐答案

您可以创建一个关联的类型和特征,指定如何从存储中检索适当类型的Vec,并将实现该特征的类型设置为Object特征的关联类型:

trait StoreGet<T> {
    fn raw_storage(store: &Store) -> &[T];
}

trait Object: Sized {
    type StoreGet: StoreGet<Self>;
    // more behavior
}

由于特征是泛型的,它的行为就好像每种类型都有单独的特征.因此,您可以使用单一类型,并实现所需类型的特性.例如:

// Your store type:
#[derive(Default)]
struct Store {
    a_store: Vec<ObjectA>,
    b_store: Vec<ObjectB>,
}

// A type that knows how to retrieve `&[ObjectA]` and `&[ObjectB]`
// from Store:
struct ObjectStoreGet;
impl StoreGet<ObjectA> for ObjectStoreGet {
    fn raw_storage(store: &Store) -> &[ObjectA] {
        &store.a_store
    }
}
impl StoreGet<ObjectB> for ObjectStoreGet {
    fn raw_storage(store: &Store) -> &[ObjectB] {
        &store.b_store
    }
}

// ObjectA and ObjectB name that type as StoreGet:
struct ObjectA {}
impl Object for ObjectA {
    type StoreGet = ObjectStoreGet;
}

struct ObjectB {}
impl Object for ObjectB {
    type StoreGet = ObjectStoreGet;
}

最后,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> {
        // ... much the same as create_object_a ...
    }

    pub fn get<Obj: Object>(&self, id: Id<Obj>) -> &Obj {
        &Obj::StoreGet::raw_storage(self)[id.raw_id]
    }
}

Playground

请注意,您应该查看an arena crate,而不是使用自己的Id+Vec(尽管如果您自己实现它,这并不是一个悲剧——重用它只是一个好主意).

Rust相关问答推荐

在Rust中,有没有一种方法让我定义两个 struct ,其中两个都遵循标准 struct ?

如何从铁 rust 中呼唤_mm_256_mul_ph?

在自身功能上实现类似移动的行为,以允许通过大小的所有者进行呼叫(&;mut;self)?

关于如何初始化弱 struct 字段的语法问题

无法理解铁 rust &S错误处理

如何提高自定义迭代器的`extend`性能

替换可变引用中的字符串会泄漏内存吗?

无法定义名为&new&的关联函数,该函数的第一个参数不是self

仅发布工作区的二进制 crate

在Rust中实现Trie数据 struct 的更好方式

如何将 &[T] 或 Vec<T> 转换为 Arc<Mutex<[T]>>?

如何从 rust 中的同一父目录导入文件

如何使用泛型满足 tokio 异步任务中的生命周期界限

Rust 函数指针似乎被borrow 判断器视为有状态的

If let expression within .iter().any

当我在 struct 中存储异步函数时,为什么它需要生命周期

我如何将 google_gmail1::Gmail> 传递给线程生成?

提取 struct 生成宏中字段出现的索引

Iterator::collect如何进行转换?

返回 &str 但不是 String 时,borrow 时间比预期长