鉴于以下(操场):
enum A { A = 0, B, C }
enum B { A = 1, B, C }
Run Code Online (Sandbox Code Playgroud)
如何锈代表类型A,Option<A>,B并Option<B>在内存中?Rust 如何将所有这四个作为单字节表示?这是有道理的,Option<B>但我希望Option<A>是两个字节。
免责声明:无法保证此处看到的确切行为将始终适用。您不应依赖特定值。此外,此答案仅基于观察,并且只会专门针对枚举;围绕&Ts、NonNull<T>s、NonZeroU8s(和系列)以及包含这些优化的嵌套结构存在其他内存优化。
如果您有一个简单的无嵌套结构枚举,默认行为是枚举变体从零开始并向上递增。所以第一个不可表示的位模式用作None值:
enum Simple { A, B };
println!("{}", unsafe { transmute::<Option<Simple>, u8>(None) });
// prints 2
Run Code Online (Sandbox Code Playgroud)
如果您的简单非嵌套结构枚举在前面留下一个间隙,则该None值仍将由枚举变体表示之后的第一个不可表示的位模式表示:
enum GapInFront { A = 1, B };
println!("{}", unsafe { transmute::<Option<GapInFront>, u8>(None) });
// prints 3
Run Code Online (Sandbox Code Playgroud)
如果在前面留一个间隙,并在位空间的末尾有一个变体,那么它才会使用全零作为None值:
enum ExtendsToEnd { A = 1, B = 255 };
println!("{}", unsafe { transmute::<Option<ExtendsToEnd>, u8>(None) });
// prints 0
Run Code Online (Sandbox Code Playgroud)
需要注意的一件事是,它永远不会选择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
Run Code Online (Sandbox Code Playgroud)
我的猜测是编译器不会跟踪所有可表示的位模式,而是只保留执行这些检查的范围。
如果您的枚举有一个带有嵌套枚举值的变体,它也会考虑到这一点:
enum Nested { A, B };
enum Complex { A(Nested), B };
println!("{}", unsafe { transmute::<Option<Complex>, u8>(None) });
// prints 3
Run Code Online (Sandbox Code Playgroud)
但是,如果两个变体具有值,即使它们的位模式不重叠(悲伤),它似乎也会中断:
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
Run Code Online (Sandbox Code Playgroud)
另一件要指出的事情,这不是Option;的特例。如果您定义自己的选项类型,则其行为相同:
enum MyOption<T> { None, Some(T) };
println!("{}", unsafe { transmute::<MyOption<Simple>, u8>(MyOption::None) });
// prints 2
Run Code Online (Sandbox Code Playgroud)
在操场上看到这一切。