構造体に安全なオーバーラップ/ユニオンフィールドを作成する

Aug 16 2020

C ++では、次のような構造を作成できます。

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

そのため、フィールドまたは連続した配列としてデータに簡単にアクセスできます。または、最初のフィールドへのポインターを作成し、ポインターからx連続した配列として読み取ることもできます。

列挙型があることは知っていますが、追加のオーバーヘッドを支払うことはできません。Rustでユニオンを作成できることも知っていますがunsafe、アクセスしている場所にコードを散らかす必要があります。基になるデータは常にfloatとして表されるため、コードは安全ではない#[repr(C)]ので、そうする必要はないと思います(コンパイラがフィールドの順序をスローしないように、Cレイアウトが必要です)。

これをRustに実装して、名前でフィールドにアクセスできるだけでなく、構造体の連続するメモリ全体に簡単かつ安全にアクセスできるようにするにはどうすればよいですか?これが不可能な場合、構造体のスライスを安全に取得できる方法はありますか?

回答

3 Locke Aug 17 2020 at 00:08

安全な組合というものはありません。個人的には、整数型の固定サイズの配列間の変換は安全であると考えるべきだと私は主張しますが、現時点では例外はありません。

そうは言っても、これは私の完全に100%組合ではありませんVector4。ご覧のとおりDeref、安全でないコードを非表示にして、Vector4使用されているコンテキストに基づいて構造体または配列として扱うことができるようにします。トランスミュートも理想的ではありませんが、正当化できると思います。この場合。このようなことを選択した場合はDerefMut、同様に実装することもできます。

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]
}