rust 为什么有Vec变体的枚举没有内存开销< i64>?[duplicate]

eblbsuwk  于 2023-03-12  发布在  其他
关注(0)|答案(1)|浏览(108)

此问题在此处已有答案

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)的栈内存呢?
有人能解释一下这里的内存布局是如何工作的,以及引擎盖下发生了什么吗?

rxztt3cl

rxztt3cl1#

这里有一个所谓的类Option枚举:
类似于选项的枚举是一个2变量enum,其中:

  • enum没有显式的#[repr(...)],并且
  • 一个变体具有单个字段,并且
  • 另一个变量没有字段(“单元变量”)。

(基本上,VecEnumOption<Vec<i64>>之间没有显著差异。)
进一步阅读上面的链接,我们可以看到编译器能够有效地使用有效载荷类型(Vec<i64>)的“小生境”(非法值)作为无有效载荷变体的枚举值,这被称为“判别省略”。
Option<&T>就是一个最明显的例子,因为引用不能为空,所以零值可以用来存储None变量,这使得&TOption<&T>具有相同的大小。
同样的事情也发生在这里,Vec<T>的第一个字段是RawVec<T>(一个内部类型),它的第一个字段是一个名为Unique<T>的(doc-hidden)类型:
原始非空*mut T的 Package 器...
如果编译器知道Unique<T>不能为空,那么这个字段中的空指针就是Vec<T>类型的一个小生境,因此这可以用作替代判别式,并执行判别式省略。
特别注意,“全零”不是唯一有效的利基。可以使用对于有效负载类型来说是非法值的任何位模式。例如,如果编译器知道 Package f64的类型保证所包含的值不能是NaN,则Option<f64>可以将None表示为NaN位模式。然而,“不允许空指针的类型中的空指针”无疑是最常见的利基。

相关问题