Rust 中 ZST/uninhabbited/one-representation 类型的 MaybeUninit 和初始化
MaybeUninit and initialization of ZST/uninhabbited/one-representation types in Rust
问题很简单,但我一直没能找到答案。下面这段代码是valid/safe在Rust中:
use core::mem::MaybeUninit;
// This is a ZST
#[derive(Debug)]
struct MyStruct;
// This is an uninhabited type
#[derive(Debug)]
enum MyEnum {}
#[derive(Debug)]
enum OneVariantEnum {
Variant1,
}
fn main() {
let s: MaybeUninit<MyStruct> = MaybeUninit::uninit();
println!("s: {:?}", unsafe { s.assume_init() });
let e: MaybeUninit<MyEnum> = MaybeUninit::uninit();
println!("e: {:?}", unsafe { e.assume_init() });
// and what about this?
let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });
}
MaybeUninit::assume_init()
的重点是将调用 MaybeUninit::uninit()
后初始化的内存转换为有效值。如果类型是零大小的,那么 MaybeUninit::uninit().assume_init()
(大多数类型的 UB)实际上是有效的,因为根本没有内存可以初始化。如果它无效,那么 没有 方法可以使用 MaybeUninit
创建 ZST,并且文档中没有提到这样的限制。
This issue 包含一个讨论,顺便证实了这一点。
请注意,以上仅适用于 ZST,不适用于无人居住的类型,因此永远不允许构造 MyEnum
,该示例是 UB。
以下似乎是正确的,因为 MyStruct
是 ZST:
let s: MaybeUninit<MyStruct> = MaybeUninit::uninit();
println!("s: {:?}", unsafe { s.assume_init() });
然而,构造一个无人居住类型的值总是 UB,所以以下是不正确的:
let e: MaybeUninit<MyEnum> = MaybeUninit::uninit();
println!("e: {:?}", unsafe { e.assume_init() });
在调试模式下,Rust 可能 捕捉到这个并用
恐慌
thread 'main' panicked at 'attempted to instantiate uninhabited type MyEnum
'
至于OneVariantEnum
,是ZST,所以和MyStruct
类似,以下不涉及UB:
let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });
问题很简单,但我一直没能找到答案。下面这段代码是valid/safe在Rust中:
use core::mem::MaybeUninit;
// This is a ZST
#[derive(Debug)]
struct MyStruct;
// This is an uninhabited type
#[derive(Debug)]
enum MyEnum {}
#[derive(Debug)]
enum OneVariantEnum {
Variant1,
}
fn main() {
let s: MaybeUninit<MyStruct> = MaybeUninit::uninit();
println!("s: {:?}", unsafe { s.assume_init() });
let e: MaybeUninit<MyEnum> = MaybeUninit::uninit();
println!("e: {:?}", unsafe { e.assume_init() });
// and what about this?
let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });
}
MaybeUninit::assume_init()
的重点是将调用 MaybeUninit::uninit()
后初始化的内存转换为有效值。如果类型是零大小的,那么 MaybeUninit::uninit().assume_init()
(大多数类型的 UB)实际上是有效的,因为根本没有内存可以初始化。如果它无效,那么 没有 方法可以使用 MaybeUninit
创建 ZST,并且文档中没有提到这样的限制。
This issue 包含一个讨论,顺便证实了这一点。
请注意,以上仅适用于 ZST,不适用于无人居住的类型,因此永远不允许构造 MyEnum
,该示例是 UB。
以下似乎是正确的,因为 MyStruct
是 ZST:
let s: MaybeUninit<MyStruct> = MaybeUninit::uninit();
println!("s: {:?}", unsafe { s.assume_init() });
然而,构造一个无人居住类型的值总是 UB,所以以下是不正确的:
let e: MaybeUninit<MyEnum> = MaybeUninit::uninit();
println!("e: {:?}", unsafe { e.assume_init() });
在调试模式下,Rust 可能 捕捉到这个并用
恐慌thread 'main' panicked at 'attempted to instantiate uninhabited type
MyEnum
'
至于OneVariantEnum
,是ZST,所以和MyStruct
类似,以下不涉及UB:
let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });