Создание безопасных полей перекрытия / объединения в структурах

Aug 16 2020

В C ++ я могу создавать такие структуры:

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

поэтому я могу легко получить доступ к данным как к полям или как к непрерывному массиву. В качестве альтернативы я могу просто создать указатель на первое поле xи читать из указателя как непрерывный массив.

Я знаю, что есть перечисления, но не могу оплачивать дополнительные накладные расходы. Я также знаю, что могу создавать союзы в Rust, но они требуют, чтобы я засорял свой код везде, unsafeгде я к ним обращаюсь. В чем, как мне кажется, не должно быть, поскольку код небезопасен, поскольку базовые данные всегда представлены как числа с плавающей запятой (и мне нужен C-макет, #[repr(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]
}