Rust not intialize option type

I wanted to print out the bit representation of an Option to better understand the layout. For a safety check I ran it though Miri, which reports that I am accessing uninitialized memory! I don't understand how this is possible. Why is value not get initialized? Is it being optimized away or is this a bug in Miri?

fn main() {
    let value: Option<u32> = None;
    let bits = unsafe { std::mem::transmute::<Option<u32>, u64>(value) };
    println!("{:x}", bits);
}

Option<u32> most likely has some padding bytes, which are considered uninitialized.

1 Like

In the case of None only 4 bytes are used. The size of an enum is the size of the largest payload + 4 bytes. In the case of the None only 4 bytes are used.

Never tried this, thnxs for pointing to it :slight_smile:

1 Like

Not always:

  • Enum can use so-called "niche optimization" and don't require additional space. For example, size_of::<Option<&T>>() == size_of::<&T>(), since null-pointer can be used to represent None.
  • Enum can require more or less then 4 bytes for discriminant, if this is necessary for alignment purposes: size_of::<Option<u64>>() == 16, i.e. 8 bytes for the number itself, and 8 bytes for discriminant, since u64 must by aligned to 8 bytes.
3 Likes

When I check the size it is 8. Are you saying that it only use the full 8 bytes in the Some case?

    println!("size = {}", std::mem::size_of::<Option<u32>>()); // 8

Even in the Some case, not all bits are guaranteed to be initialized.

I believe this enum uses one byte for the discriminant, three bytes for padding, and four bytes for the payload. The discriminant is always initialized. The padding is never initialized. The payload is initialized only in the Some case.

(You may observe that the padding bytes are zeroed in practice, but this is not guaranteed to always happen in every implementation of Rust.)

5 Likes

Interesting. So is there any safe way to read the bit representation of an enum? It seems like you would hit this unless you already new exactly how it was laid out (which defeats the point).

Not in the general case; you can't be sure you haven't read uninitialized data, which is undefined behavior.

Also note that the compiler is free to change how it's laid out the enum, e.g. version to version. (If you figure out how it's laid out today, it may still be laid out differently tomorrow.)

1 Like

Note that a repr(Rust) enum like Option<u32> doesn't have any layout guarantees. (Some options do have guarantees, notably Option<&T> and friends, but most don't.)

So if you're just curious about layouts, I'd suggest looking in a debugger, as that operates outside the rust abstract machine and thus is allowed to look at things in ways that would be UB to do in Rust itself.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.