我知道 Rust 结构中的字段会自动重新排序以节省内存(除非指定了
#[repr(C)]
)。
我假设枚举也是如此。 今天我正在创建一个简单的辅助类型(示例的语义并不重要,这是关于内存布局的):
pub enum LazyRwLockGuardVersionA<'a, T> {
Unlocked(&'a RwLock<T>),
Read {
lock: &'a RwLock<T>, // still neccessary for promoting to write lock
guard: RwLockReadGuard<'a, T>,
},
Write(RwLockWriteGuard<'a, T>),
}
原始读/写防护使用 16 字节,而此类型使用 32。 为了让编译器使用利基优化,我尝试了这个版本:
pub enum LazyRwLockWriteGuard<'a, T> {
Unlocked(&'a RwLock<T>),
Write(RwLockWriteGuard<'a, T>),
}
pub enum LazyRwLockGuardVersionB<'a, T> {
Read {
guard: RwLockReadGuard<'a, T>,
lock: &'a RwLock<T>,
},
NonRead(LazyRwLockWriteGuard<'a, T>),
}
这成功地将内存使用量降低到24字节。
但是,我注意到一些奇怪的事情: 当反转
Read
变体中的字段顺序时:
pub enum LazyRwLockGuardVersionC<'a, T> {
Read {
lock: &'a RwLock<T>, // order of fields reversed
guard: RwLockReadGuard<'a, T>,
},
NonRead(LazyRwLockWriteGuard<'a, T>),
}
此类型突然再次使用32字节。
我的 Rust 版本是 1.77,这是一个 godbolt 重现:https://godbolt.org/z/svE5v6Tr8
规范不允许枚举字段重新排序,还是编译器在这里只是“懒惰”?
默认/
Rust
类型布局完全未指定,超出了健全性保证。否则,编译器可以自由地做任何它想做的事情,事实上,这确实在编译器版本之间发生了变化。
对于编译器当前用于布局枚举的算法是否有一个粗略的直觉,以便我可以理解这个结果,并防止将来我的代码意外悲观?
如果想要布局有保证,就必须使用
#[repr(C)]
。其他任何事情都将由编译器随意决定。