Ted Klein Bergman
Ted Klein Bergman

Reputation: 9766

Creating safe overlapping/union fields in structs

In C++, I can create structures like these:

union Vector4
{
    struct { float x, y, z, w; };
    float data[4];
};

so I can easily access the data as fields or as an contiguous array. Alternatively, I can just create a pointer to the first field x and read from the pointer as an contiguous array.

I know that there are enums, but I can't pay for the additional overhead. I also know I can create unions in Rust, but they require me to litter my code with unsafe where ever I'm accessing them. Which I feel I shouldn't have to since the code is not unsafe as the underlying data is always represented as floats (and I need the C-layout #[repr(C)] so the compiler won't throw around the order of the fields).

How would I implement this in Rust so that I can access the fields by name but also have easy and safe access to the whole struct's contiguous memory? If this is not possible, is there a way so I can safely take a slice of a struct?

Upvotes: 2

Views: 880

Answers (1)

Locke
Locke

Reputation: 8980

There is no such thing as a safe union. Personally, I would argue that transmuting between fixed sized arrays of integer types should be considered safe, but at the moment there are no exceptions.

That being said, here is my totally 100% not a union Vector4. As you can see, Deref works to hide the unsafe code and makes it so you can treat Vector4 as either a struct or an array based on the context it is used in. The transmute also isn't ideal, but I feel like I can justify it in this case. If you choose to do something like this, then you may also want to implement DerefMut as well.

use std::ops::Deref;

// I'm not sure if the repr(C) is needed in this case, but I added it just in case.
#[repr(C)]
pub struct Vector4<T> {
    pub x: T,
    pub y: T,
    pub z: T,
    pub w: T,
}

impl<T> Deref for Vector4<T>
where
    T: Copy + Sized,
{
    type Target = [T; 4];

    fn deref(&self) -> &Self::Target {
        use std::mem::transmute;
        unsafe { transmute(self) }
    }
}

pub fn main() {
    let a = Vector4{
        x: 37,
        y: 21,
        z: 83,
        w: 94,
    };

    println!("{:?}", &a[..]);
    // Output: [37, 21, 83, 94]
}

Upvotes: 4

Related Questions