首页 > 解决方案 > enum和Option的内存表示是什么?

问题描述

鉴于以下(游乐场):

enum A { A = 0, B, C }
enum B { A = 1, B, C }

Rust 如何表示内存中的类型A、、Option<A>和?Rust 如何能够将所有这四个都作为单字节表示?这是有道理的,但我预计是两个字节。BOption<B>Option<B>Option<A>

标签: memoryrust

解决方案


免责声明:不能保证此处看到的确切行为将始终成立。您不应该依赖特定的值。此外,这个答案仅基于观察,并且只会专门针对枚举;&T围绕s、NonNull<T>s、NonZeroU8s(和 family)以及包含这些的嵌套结构存在其他内存优化。


基本枚举

如果您有一个简单的非嵌套结构枚举,则默认行为是枚举变量从零开始并向上递增。所以第一个不可表示的位模式被用作None值:

enum Simple { A, B };
println!("{}", unsafe { transmute::<Option<Simple>, u8>(None) });
// prints 2

如果您的简单非嵌套结构枚举在前面留下一个间隙,则该None值仍将由枚举变体表示之后的第一个不可表示的位模式表示:

enum GapInFront { A = 1, B };
println!("{}", unsafe { transmute::<Option<GapInFront>, u8>(None) });
// prints 3

如果您在前面留下一个间隙,并且在位空间的末尾有一个变体,那么它才会使用全零作为None值:

enum ExtendsToEnd { A = 1, B = 255 };
println!("{}", unsafe { transmute::<Option<ExtendsToEnd>, u8>(None) });
// prints 0

需要注意的一点是,它永远不会在值的变体之间选择表示形式None。即使有大量不可表示的位模式,占据边界的变体也会导致它使用 2 字节:

enum Full { A = 0, B = 255 };
println!("{:?}", unsafe { transmute::<Option<Full>, [u8; 2]>(None) });
// prints [0, 60], which I believe is undefined behavior 
// since I think the second byte is left uninitialized

我的猜测是编译器不会跟踪所有可表示的位模式,而是只保留进行这些检查的范围。


更多有趣的东西。

如果您的枚举具有嵌套枚举值的变体,它也会考虑到这一点:

enum Nested { A, B };
enum Complex { A(Nested), B };
println!("{}", unsafe { transmute::<Option<Complex>, u8>(None) });
// prints 3

但是,如果两个变体具有值,即使它们的位模式不重叠(悲伤),它似乎也会中断:

enum Nested1 { A, B };
enum Nested2 { A=2, B };
enum MoreComplex { A(Nested1), B(Nested2) };
println!("{:?}", unsafe { transmute::<Option<MoreComplex>, [u8; 2]>(None) });
// prints [2, 211], again the second byte is left uninitialized

需要指出的另一件事,这不是 ; 的特殊情况Option。如果您定义自己的选项类型,它的行为相同:

enum MyOption<T> { None, Some(T) };
println!("{}", unsafe { transmute::<MyOption<Simple>, u8>(MyOption::None) });
// prints 2

在操场上看到这一切。

另请参阅Rust 的 Option 类型的开销是多少?.


推荐阅读