这个问题很简单,但是我一直没能找到答案。以下代码在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() });
}
这似乎是正确的,因为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可能会捕捉到这个错误,并使用
panic线程'main'在'试图实例化无人类型
MyEnum
'时恐慌
对于OneVariantEnum
,它是一个ZST,所以它与MyStruct
类似,下面不涉及UB:
let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });
MaybeUninit::assume_init()
的重点是将调用MaybeUninit::uninit()
后初始化的内存转换为有效值。如果类型的大小为零,那么MaybeUninit::uninit().assume_init()
(对于大多数类型来说是UB)实际上是有效的,因为根本没有内存可以初始化。如果它无效,那么就没有方法可以使用MaybeUninit
创建zst,并且文档中没有提到这种限制。
这个问题包含了一个讨论,顺便确认了这一点。
请注意,以上只适用于zst,而不适用于无人居住的类型,因此永远不允许构造MyEnum
,该示例是UB。