Rust中ZST/ un人居/one-representation类型的uninit和初始化



这个问题很简单,但是我一直没能找到答案。以下代码在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。

最新更新