Mapping nested packed union from C to Rust

I'm attempting to map the MTLPackedFloat3 type from C to Rust:

typedef struct _MTLPackedFloat3 {
    union {
        struct {
            float x;
            float y;
            float z;
        };
        float elements[3];
    };
} MTLPackedFloat3;

The naive result is this, which I'm fairly sure is right:

use core::ffi::c_float;

#[repr(C)]
pub struct MTLPackedFloat3Inner {
    x: c_float,
    y: c_float,
    z: c_float,
}

#[repr(C)]
pub union MTLPackedFloat3 {
    struct_: MTLPackedFloat3Inner,
    elements: [c_float; 3],
}

But since the elements view is uninteresting to Rust users, I'm wondering if I could simply define it as follows, and have it behave the same way?

use core::ffi::c_float;

#[repr(C)]
pub struct MTLPackedFloat3 {
    x: c_float,
    y: c_float,
    z: c_float,
}

Is there something I'm missing? All three seem to have a size of 12 and an alignment of 4. The "Packed" part of the name somewhat implies that I should use #[repr(packed)], which lowers the alignment to 1, but I'm unsure if that's correct?

Perhaps the C structure is intended to be a promise not to touch the padding bytes, whereas it would otherwise be allowed to? Is there a similar way to make the Rust compiler guarantee such a thing?

In context, I believe it means that the vector is not padded to 4 elements (a common requirement in GPU memory layout), i.e. that it is 12 bytes and not 16. You should not decrease the alignment.

But since the elements view is uninteresting to Rust users,

Converting between different types of vectors can be useful (for efficient library interop and CPU↔GPU copying). But instead of using a union (which is unsafe since the compiler doesn't know you're doing something that's just as good either way) I would suggest sticking to one choice of representation (either the struct or the array) and implementing bytemuck::Pod for your type, so that it (or a slice of it) can be converted to any other type for vectors of 3 floats, using entirely safe code.

2 Likes