Marin Veršić
Marin Veršić

Reputation: 403

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

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() });
}

Upvotes: 2

Views: 336

Answers (2)

user4815162342
user4815162342

Reputation: 155236

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.

Upvotes: 0

mcarton
mcarton

Reputation: 30061

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() });

Upvotes: 2

Related Questions