Membuat bidang tumpang tindih / serikat yang aman di struct

Aug 16 2020

Di C ++, saya dapat membuat struktur seperti ini:

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

jadi saya dapat dengan mudah mengakses data sebagai kolom atau sebagai array yang berdekatan. Atau, saya bisa membuat penunjuk ke bidang pertama xdan membaca dari penunjuk sebagai array yang berdekatan.

Saya tahu bahwa ada enum, tetapi saya tidak dapat membayar biaya overhead tambahan. Saya juga tahu bahwa saya dapat membuat serikat di Rust, tetapi mereka mengharuskan saya membuang kode saya di unsafemana pun saya mengaksesnya. Yang saya rasa saya tidak perlu melakukannya karena kodenya tidak tidak aman karena data yang mendasarinya selalu direpresentasikan sebagai float (dan saya membutuhkan C-layout #[repr(C)]sehingga kompiler tidak akan mengubah urutan bidang).

Bagaimana saya menerapkan ini di Rust sehingga saya dapat mengakses bidang dengan nama tetapi juga memiliki akses yang mudah dan aman ke seluruh memori bersebelahan struct? Jika ini tidak memungkinkan, apakah ada cara agar saya dapat dengan aman mengambil sepotong struct?

Jawaban

3 Locke Aug 17 2020 at 00:08

Tidak ada yang namanya persatuan yang aman. Secara pribadi, saya berpendapat bahwa transmutasi antara array berukuran tetap dari tipe integer harus dianggap aman, tetapi saat ini tidak ada pengecualian.

Yang sedang berkata, di sini saya benar-benar 100% bukan serikat pekerja Vector4. Seperti yang Anda lihat, Derefberfungsi untuk menyembunyikan kode yang tidak aman dan membuatnya sehingga Anda dapat memperlakukan Vector4baik struct atau array berdasarkan konteks penggunaannya. Transmutasinya juga tidak ideal, tapi saya merasa saya bisa membenarkannya pada kasus ini. Jika Anda memilih untuk melakukan sesuatu seperti ini, Anda mungkin juga ingin menerapkannya 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]
}