use std::mem::size_of;
struct Position {
x: f32,
y: f32,
z: f32,
}
struct PoolItem {
entity_id: u32, // 4 bytes
used: bool, // 1 bytes + 3 (padding)
component: Position, // 12 bytes
}
assert_eq!(size_of::<u32>(), 4);
assert_eq!(size_of::<Position>(), 12);
assert_eq!(size_of::<PoolItem>(), 20);
如您所见,这种结构的长度为20个字节。
Position
实际上是 optional ,并且取决于used
。使用
Option
是否会消除对used
字段的需要并将结构大小减小到16个?struct PoolItem {
entity_id: u32, // 4 bytes
component: Option<Position>, // 12 bytes ?
}
如果是这样,如何实现这种行为才能实现
Option
?我对Playground的测试似乎表明它不起作用。为什么?
最佳答案
Option<Position>
需要将状态(Some
或None
)存储在某个位置,并且由于Position
已经包含12个字节的信息,因此需要更多空间来存储它。通常这意味着它增加了一个额外的字节(加上填充)来存储状态,尽管在某些情况下内部类型具有已知的未使用状态。例如,引用可以指向地址0
,因此Option<&'_ T>
可以使用0
作为None
状态,并占用与&'_ T
相同的字节数。但是,对于您的Position
类型,情况并非如此。
如果您绝对需要PoolItem
结构尽可能小,并且可以从entity_id
字段中保留一位(例如最高位231),则可以使用它来存储状态:
const COMPONENT_USED_BIT: u32 = (1u32 << 31);
struct PoolItem {
entity_id: u32, // lowest 31 bits = entity ID, highest bit = "component used"
component: Position,
}
这可能会变得有些复杂,因为您需要确保特别对待该位,但是您可以编写一些简单的访问器方法来确保正确处理该特殊位。
impl PoolItem {
/// Get entity ID, without the "component used" bit
fn entity_id(&self) -> u32 {
self.entity_id & !COMPONENT_USED_BIT
}
/// Set entity ID, keeping the existing "component used" bit
fn set_entity_id(&mut self, entity_id: u32) {
let component_used_bit = self.entity_id & COMPONENT_USED_BIT;
self.entity_id = (entity_id & !COMPONENT_USED_BIT) | component_used_bit;
}
/// Get component if "component used" bit is set
fn component(&self) -> Option<&Position> {
if self.entity_id & COMPONENT_USED_BIT != 0 {
Some(&self.component)
} else {
None
}
}
/// Set component, updating the "component used" bit
fn set_component(&mut self, component: Option<Position>) {
if let Some(component) = component {
self.component = component;
self.entity_id |= COMPONENT_USED_BIT;
} else {
self.entity_id &= !COMPONENT_USED_BIT;
}
}
}
Playground example with tests
关于struct - 选项类型是否小于包装类型加上 boolean 值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61043096/