Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does making one enum variant an `f64` increase the size of this enum?

Tags:

enums

rust

I have created three enums that are nearly identical:

#[derive(Clone, Debug)]
pub enum Smoller {
    Int(u8),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

#[derive(Clone, Debug)]
pub enum Smol {
    Float(f32),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

#[derive(Clone, Debug)]
pub enum Big {
    Float(f64),
    Four([u8; 4]),
    Eight([u8; 8]),
    Twelve([u8; 12]),
    Sixteen([u8; 16]),
}

pub fn main() {
    println!("Smoller: {}", std::mem::size_of::<Smoller>()); // => Smoller: 17
    println!("Smol: {}", std::mem::size_of::<Smol>()); // => Smol: 20
    println!("Big: {}", std::mem::size_of::<Big>()); // => Big: 24
}

What I expect, given my understanding of computers and memory, is that these should be the same size. The biggest variant is the [u8; 16] with a size of 16. Therefore, while these enums do have a different size first variant, they have the same size of their biggest variants and the same number of variants total.

I know that Rust can do some optimizations to acknowledge when some types have gaps (e.g. pointers can collapse because we know that they won't be valid and 0), but this is really the opposite of that. I think if I were constructing this enum by hand, I could fit it into 17 bytes (only one byte being necessary for the discrimination), so both the 20 bytes and the 24 bytes are perplexing to me.

I suspect this might have something to do with alignment, but I don't know why and I don't know why it would be necessary.

Can someone explain this?

Thanks!

like image 353
River Tam Avatar asked Dec 02 '22 10:12

River Tam


1 Answers

The size must be at least 17 bytes, because its biggest variant is 16 bytes big, and it needs an extra byte for the discriminant (the compiler can be smart in some cases, and put the discriminant in unused bits of the variants, but it can't do this here).

Also, the size of Big must be a multiple of 8 bytes to align f64 properly. The smaller multiple of 8 bigger than 17 is 24. Similarly, Smol cannot be only 17 bytes, because its size must be a multiple of 4 bytes (the size of f32). Smoller only contains u8 so it can be aligned to 1 byte.

like image 85
mcarton Avatar answered Jan 12 '23 23:01

mcarton