此问题在此处已有答案:
What is the null pointer optimization in Rust?(3个答案)
How is TinyVec the same size as Vec?(1个答案)
昨天关门了。
我不明白为什么下面代码中Vec和VecEnum的大小相同:
pub enum VecEnum {
Abc,
Vec(Vec<i64>),
}
pub enum IntEnum {
Abc,
Int(i64),
}
pub fn main() {
println!("IntEnum: {} bytes", core::mem::size_of::<IntEnum>());
println!("i64: {} bytes", core::mem::size_of::<i64>());
println!("VecEnum: {} bytes", core::mem::size_of::<VecEnum>());
println!("Vec<i64>: {} bytes", core::mem::size_of::<Vec<i64>>());
}
这将输出以下内容:
IntEnum: 16 bytes
i64: 8 bytes
VecEnum: 24 bytes
Vec<i64>: 24 bytes
对于i64,其表现符合预期:拥有一个i64变体的枚举需要额外的空间来编码枚举标记。2但是为什么Vec不是这样,它只包含3个8字节值(ptr,len,capacity)的栈内存呢?
有人能解释一下这里的内存布局是如何工作的,以及引擎盖下发生了什么吗?
1条答案
按热度按时间rxztt3cl1#
这里有一个所谓的类Option枚举:
类似于选项的枚举是一个2变量
enum
,其中:enum
没有显式的#[repr(...)]
,并且(基本上,
VecEnum
和Option<Vec<i64>>
之间没有显著差异。)进一步阅读上面的链接,我们可以看到编译器能够有效地使用有效载荷类型(
Vec<i64>
)的“小生境”(非法值)作为无有效载荷变体的枚举值,这被称为“判别省略”。Option<&T>
就是一个最明显的例子,因为引用不能为空,所以零值可以用来存储None
变量,这使得&T
和Option<&T>
具有相同的大小。同样的事情也发生在这里,
Vec<T>
的第一个字段是RawVec<T>
(一个内部类型),它的第一个字段是一个名为Unique<T>
的(doc-hidden)类型:原始非空
*mut T
的 Package 器...如果编译器知道
Unique<T>
不能为空,那么这个字段中的空指针就是Vec<T>
类型的一个小生境,因此这可以用作替代判别式,并执行判别式省略。特别注意,“全零”不是唯一有效的利基。可以使用对于有效负载类型来说是非法值的任何位模式。例如,如果编译器知道 Package
f64
的类型保证所包含的值不能是NaN
,则Option<f64>
可以将None
表示为NaN
位模式。然而,“不允许空指针的类型中的空指针”无疑是最常见的利基。