Flat struct fields?

Normal Rust structs are allowed to reorder their fields as they like. So is it worth having a way to flatten Option<> values and the like, to save some space in structs?

#![allow(dead_code, unused_variables)]

struct Foo1 {
    data1: u32,
    data1_is_present: bool,
    more_data: u16
}

struct Foo2 { // Equivalent to Foo1.
    //#[flat]
    data1: Option<u32>,
    more_data: u16
}

fn main() {
    println!("{}", std::mem::size_of::<Foo1>()); // 8 bytes
    println!("{}", std::mem::size_of::<Foo2>()); // 12 bytes

    let mut f2 = Foo2 { data1: None, more_data: 0 };
    f2.data1 = Some(10); // OK
    let f2r = &f2.data1; // Error, disallowed if #[flat].
}

I understand why this is necessary to forbid, but it's a rather large restriction, because it also disallows all &self and &mut self methods of Option.

You can also get your flattened effect if you use a custom enum for the whole thing instead. I'll grant you this would be kind of ugly to work with though:

enum Foo3 {
    WithData1 { more_data: u16, data1: u32 },
    WithoutData1 { more_data: u16 },
}

Note that discriminant tags always go up front right now (see rfcs#1230), so you have to reorder the fields to make room for the tag to push into some alignment padding. If we left data1: u32 first, the tag has to add a whole 4 bytes to keep it aligned.

1 Like

Actually, we shouldn't really need the exoticism of rfcs#1230 to compact my Foo3 regardless of order. It should be perfectly legal for the compiler to reorder that implicitly, but anecdotally it does not.