MaybeUninit and initialization of ZST/uninhabbited/one-representation types in Rust

Viewed 180

The question is simple but I haven't been able to find the answer. Is the following piece of code valid/safe in 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() });
}
2 Answers

This following seems correct, because MyStruct is a ZST:

let s: MaybeUninit<MyStruct> = MaybeUninit::uninit();
println!("s: {:?}", unsafe { s.assume_init() });

Constructing a value of an uninhabited type is however always UB, so the following is not correct:

let e: MaybeUninit<MyEnum> = MaybeUninit::uninit();
println!("e: {:?}", unsafe { e.assume_init() });

In debug mode, Rust might catch this and panic with

thread 'main' panicked at 'attempted to instantiate uninhabited type MyEnum'


As for OneVariantEnum, it is a ZST, so it is similar to MyStruct and the following does not involve UB:

let o: MaybeUninit<OneVariantEnum> = MaybeUninit::uninit();
println!("o: {:?}", unsafe { o.assume_init() });

The whole point of MaybeUninit::assume_init() is to convert memory initialized after the call to MaybeUninit::uninit() into valid values. If the type is zero-sized, then MaybeUninit::uninit().assume_init() (UB for most types) is actually valid because there is simply no memory to initialize. If it weren't valid, then there would be no way to create ZSTs using MaybeUninit, and such a limitation is not mentioned in the documentation.

This issue contains a discussion that confirms this in passing.

Note that the above only applies to ZSTs, not to uninhabited types, so constructing MyEnum is never allowed and that example is UB.

Related