Создание безопасных полей перекрытия / объединения в структурах
В C ++ я могу создавать такие структуры:
union Vector4
{
struct { float x, y, z, w; };
float data[4];
};
поэтому я могу легко получить доступ к данным как к полям или как к непрерывному массиву. В качестве альтернативы я могу просто создать указатель на первое поле x
и читать из указателя как непрерывный массив.
Я знаю, что есть перечисления, но не могу оплачивать дополнительные накладные расходы. Я также знаю, что могу создавать союзы в Rust, но они требуют, чтобы я засорял свой код везде, unsafe
где я к ним обращаюсь. В чем, как мне кажется, не должно быть, поскольку код небезопасен, поскольку базовые данные всегда представлены как числа с плавающей запятой (и мне нужен C-макет, #[repr(C)]
чтобы компилятор не менял порядок полей).
Как мне реализовать это в Rust, чтобы я мог получать доступ к полям по имени, а также иметь простой и безопасный доступ ко всей непрерывной памяти всей структуры? Если это невозможно, есть ли способ безопасно взять кусок структуры?
Ответы
Безопасного союза не существует. Лично я бы сказал, что преобразование между массивами фиксированного размера целочисленных типов следует считать безопасным, но на данный момент нет никаких исключений.
При этом, вот мой полностью 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]
}