Tworzenie bezpiecznych pól nakładających się / sumujących w strukturach

Aug 16 2020

W C ++ mogę tworzyć takie struktury:

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

dzięki czemu mogę łatwo uzyskać dostęp do danych w postaci pól lub w postaci ciągłej tablicy. Alternatywnie mogę po prostu utworzyć wskaźnik do pierwszego pola xi odczytać ze wskaźnika jako ciągłą tablicę.

Wiem, że są wyliczenia, ale nie mogę zapłacić za dodatkowe koszty. Wiem też, że mogę tworzyć związki w Rust, ale wymagają one ode mnie zaśmiecania mojego kodu unsafewszędzie tam, gdzie mam do nich dostęp. Czego uważam, że nie powinienem tego robić, ponieważ kod nie jest niebezpieczny, ponieważ podstawowe dane są zawsze reprezentowane jako elementy zmiennoprzecinkowe (i potrzebuję układu C, #[repr(C)]aby kompilator nie zmienił kolejności pól).

Jak zaimplementowałbym to w Rust, aby mieć dostęp do pól według nazwy, ale także mieć łatwy i bezpieczny dostęp do ciągłej pamięci całej struktury? Jeśli nie jest to możliwe, czy jest sposób, abym mógł bezpiecznie wziąć kawałek struktury?

Odpowiedzi

3 Locke Aug 17 2020 at 00:08

Nie ma czegoś takiego jak bezpieczny związek. Osobiście uważałbym, że transmutacja między tablicami o ustalonych rozmiarach typów całkowitych powinno być uważane za bezpieczne, ale w tej chwili nie ma wyjątków.

Biorąc to pod uwagę, oto mój całkowicie 100% nie związek Vector4. Jak widać, Derefdziała w celu ukrycia niebezpiecznego kodu i sprawia, że ​​można go traktować Vector4jako strukturę lub tablicę w oparciu o kontekst, w którym jest używany. Transmutowanie również nie jest idealne, ale czuję, że mogę to uzasadnić w tym przypadku. Jeśli zdecydujesz się zrobić coś takiego, możesz również chcieć to zaimplementować 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]
}