目前在我的项目中,我有一个结构体,看起来像这样:
pub struct Ptr<T>(Rc<RefCell<Option<T>>>);
pub(crate) type EntList<T> = Vec<Ptr<T>>;
struct Entities {
pub entity_a: EntList<EntityA>,
pub entity_b: EntList<EntityB>,
pub entity_c: EntList<EntityC>,
// ..etc
}
impl Entities {
pub fn from_rows(locations: &TableSizes) -> Self {
fn init_vec<T>(size: TableSize) -> EntList<T> {
let mut v = EntList::with_capacity(size.0);
for _ in 0..v.capacity() {
v.push(Ptr::default());
}
v
}
Self {
entity_a: init_vec(sizes.entity_a),
entity_b: init_vec(locations.entity_b),
entity_c: init_vec(locations.entity_c),
}
}
fn init_entities(&self, entity_reader: &EntityReader) -> Result<()> {
fn init_entity(uninit_rows: &EntList<E>, entity_reader: EntityReader, entities: &Entities) -> Result<()> {
for entity in uninit_rows {
let real_value = entity_reader.read::<T>(entities)?;
entity.0.replace(Some(real_value));
}
}
}
}
let entities = Entities::from_rows(&table_sizes);
entities.init_entities()?;
真实的上,Entity
结构中有52个EntList
字段。
我这样做的原因是因为当实体实际上通过init_entity
加载时,实体可以在它们存在之前引用其他实体。例如,EntityA
可能引用entity_c
列表中的EntityC
,实体EntityB
可能引用EntityA
。
这个系统 * 确实有效 *,但我的问题是,当所有实体都被加载时,在任何EntList
中都没有None
值。因此,为了使这个API更好地使用,我想在解析完成后删除它。我的想法是使用MaybeIninit
来代替Ptr
结构中的Option
。
问题是引用可能卸载的实体的每个实体将持有Ref<T>
,而不是RefCell<MaybeUninit<T>>
。
我的问题是,如果RefCell
从未被解除引用,那么当数据未初始化时,克隆RefCell<MaybeUninit<T>>
并将其转换为RefCell<T>
是否安全?
如果它是不安全的,是否可以将所有指针存储为RefCell<MaybeUninit<T>>
,然后在加载它们时将它们转换为RefCell<T>
?
额外信息:没有办法重新排列它们的加载顺序,以避免引用未加载的实体,并且这种结构非常严格,因为它是一种文件格式。
我也一直试图解决这个解决方案与GAT的,但我不认为它会工作,因为MaybeUninit<T>
dosent impl Clone
,除非T: Copy
和有情况下,它不是。但我需要我的实体是`克隆。
1条答案
按热度按时间pcww981p1#
不允许。
RefCell
是#[repr(Rust)]
(默认表示),因此,它的字段顺序是不保证的。即使MaybeUninit<T>
保证具有与T
相同的布局,这也不适用于包含它的类型。MaybeUninit
文档中明确规定了此警告:虽然
MaybeUninit
是#[repr(transparent)]
(表示它保证与T
相同的大小,对齐和ABI),但这并没有改变任何先前的警告。Option<T>
和Option<MaybeUninit<T>>
仍然可以具有不同的大小,并且包含类型T
的字段的类型可以与如果该字段是MaybeUninit<T>
时不同地布局(和大小)。但是,假设组件一旦创建就不可变,则有一种解决方案。
可以使用
Cell
代替RefCell
。Cell<T>
does have the same memory layout asT
(尽管这只是目前在beta版和nightly上指定的,但我相信即使在稳定版上也可以依赖它),因此可以将Rc<Cell<MaybeUninit<T>>>
转换为Rc<Cell<T>>
或Rc<T>
。因为你只需要replace()
(或者实际上是set()
,因为你没有使用返回值),这应该对你有用。注意:Rc
本身的布局是未指定的,所以你 * 不能 * 将Rc<Cell<MaybeUninit<T>>>
转换为Rc<T>
,但你 * 可以 * 使用Rc::into_raw()
和Rc::from_raw()
,并在两者之间转换指针。确保在引用其他组件的组件中存储
Rc<T>
而不是Rc<Cell<T>>
,这样就不能改变它们。