我正在尝试在Rust中实现基本的ECS。我想要一个数据结构,为每个组件存储该特定组件的存储。由于某些组件很常见而其他组件很少,因此我需要不同类型的存储策略,例如VecStorage<T>
和HashMapStorage<T>
。
由于游戏引擎的ECS不知道组件,我想出了:
trait AnyStorage: Debug {
fn new() -> Self
where
Self: Sized;
}
#[derive(Default, Debug)]
struct StorageMgr {
storages: HashMap<TypeId, Box<AnyStorage>>,
}
VecStorage
和HashMapStorage<T>
实施AnyStorage
特征。由于AnyStorage
不知道T
,我添加了另外一个具体存储空间实现的特征:ComponentStorage<T>
。
虽然我能够注册新组件(即在Box<AnyStorage>
的{{1}}中添加新的StorageMgr
),但我找不到插入组件的方法。
这是错误的代码:
storages
我知道我的问题来自pub fn add_component_to_storage<C: Component>(&mut self, component: C) {
let storage = self.storages.get_mut(&TypeId::of::<C>()).unwrap();
// storage is of type: &mut Box<AnyStorage + 'static>
println!("{:?}", storage); // Prints "VecStorage([])"
storage.insert(component); // This doesn't work
// This neither:
// let any_stor: &mut Any = storage;
// let storage = any_stor.downcast_ref::<ComponentStorage<C>>();
}
的类型为storage
的事实;我能从中获得具体的&mut Box<AnyStorage>
吗?
完成所有这一切的重点在于我希望组件在内存中是连续的,并且每个组件类型都有不同的存储空间。我无法解决自己使用VecStorage
,或者我不知道如何使用。
我将问题缩减为最小代码on Rust Playground。
答案 0 :(得分:3)
我不确定这样的事情是否可行,但我终于明白了。关于你发布的示例失败的原因,有几点需要注意。
AnyStorage
未实现ComponentStorage<T>
,因此您将“存储”存储在HashMap<TypeId, Box<AnyStorage>>
中,Rust无法保证每个存储的类型都已实现{{ 1}}因为它只知道它们是ComponentStorage<T>::insert()
s。AnyStorage
的特征并将其存储在Storage<T>
中,则HashMap<TypeId, Box<Storage<T>>
的每个版本都必须存储相同的类型,因为单{{ {1}}。 Rust没有办法根据键的TypeId动态键入映射的值,因为这样的解决方案需要。此外,您无法将Storage
替换为T
,因为T
不是Any
,Any
和所有其他存储类型都需要。我猜你知道这一切,这就是为什么你在原始例子中使用了两个不同的特征。我最终使用的解决方案将Sized
s存储为Vec
中的Storage<T>
,然后我将Any
下载到HashMap<TypeId, Box<Any>>
s在Any
的实现函数中。我在下面给出了一个简短的例子,完整版本在Rust Playground上here
Storage<T>