repr(Rust)

    Primitives are usually aligned to their size, although this isplatform-specific behavior. For example, on x86 u64 and f64 are oftenaligned to 4 bytes (32 bits).

    A type’s size must always be a multiple of its alignment. This ensures that anarray of that type may always be indexed by offsetting by a multiple of itssize. Note that the size and alignment of a type may not be knownstatically in the case of dynamically sized types.

    Rust gives you the following ways to lay out composite data:

    • structs (named product types)
    • tuples (anonymous product types)
    • arrays (homogeneous product types)
    • enums (named sum types — tagged unions)
    • unions (untagged unions)

    An enum is said to be field-less if none of its variants have associated data.

    By default, composite structures have an alignment equal to the maximumof their fields’ alignments. Rust will consequently insert padding wherenecessary to ensure that all fields are properly aligned and that the overalltype’s size is a multiple of its alignment. For instance:

    1. struct A {
    2. a: u8,
    3. _pad1: [u8; 3], // to align `b`
    4. b: u32,
    5. c: u16,
    6. _pad2: [u8; 2], // to make overall size multiple of 4
    7. }

    or maybe:

    There is no indirection for these types; all data is stored within the struct,as you would expect in C. However with the exception of arrays (which aredensely packed and in-order), the layout of data is not specified by default.Given the two following struct definitions:

    1. a: i32,
    2. b: u64,
    3. }
    4. a: i32,
    5. b: u64,
    6. }

    Rust does guarantee that two instances of A have their data laid out inexactly the same way. However Rust does not currently guarantee that aninstance of A has the same field ordering or padding as an instance of B.

    With A and B as written, this point would seem to be pedantic, but several otherfeatures of Rust make it desirable for the language to play with data layout incomplex ways.

    For instance, consider this struct:

    1. struct Foo<u16, u32> {
    2. count: u16,
    3. data1: u16,
    4. data2: u32,
    5. struct Foo<u32, u16> {
    6. count: u16,
    7. _pad1: u16,
    8. data1: u32,
    9. _pad2: u16,
    10. }

    The latter case quite simply wastes space. An optimal use of spacerequires different monomorphizations to have different field orderings.

    Enums make this consideration even more complicated. Naively, an enum such as:

    might be laid out as:

    1. struct FooRepr {
    2. data: u64, // this is either a u64, u32, or u8 based on `tag`
    3. tag: u8, // 0 = A, 1 = B, 2 = C
    4. }

    And indeed this is approximately how it would be laid out (modulo thesize and position of tag).

    However there are several cases where such a representation is inefficient. Theclassic case of this is Rust’s “null pointer optimization”: an enum consistingof a single outer unit variant (e.g. None) and a (potentially nested) non-nullable pointer variant (e.g. Some(&T)) makes the tag unnecessary. A nullpointer can safely be interpreted as the unit (None) variant. The netresult is that, for example, size_of::<Option<&T>>() == size_of::<&T>().