Criação de campos de sobreposição / união seguros em estruturas

Aug 16 2020

Em C ++, posso criar estruturas como estas:

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

para que eu possa acessar facilmente os dados como campos ou como uma matriz contígua. Como alternativa, posso apenas criar um ponteiro para o primeiro campo xe ler a partir do ponteiro como uma matriz contígua.

Eu sei que existem enums, mas não posso pagar pela sobrecarga adicional. Eu também sei que posso criar sindicatos em Rust, mas eles exigem que eu espalhe meu código unsafeonde quer que eu esteja acessando. O que eu sinto que não deveria, já que o código não é inseguro, pois os dados subjacentes são sempre representados como flutuantes (e eu preciso do layout C #[repr(C)]para que o compilador não divulgue a ordem dos campos).

Como eu implementaria isso no Rust para poder acessar os campos pelo nome, mas também ter acesso fácil e seguro a toda a memória contígua do struct? Se isso não for possível, há uma maneira de pegar com segurança uma fatia de uma estrutura?

Respostas

3 Locke Aug 17 2020 at 00:08

Não existe união segura. Pessoalmente, eu diria que a transmutação entre matrizes de tamanho fixo de tipos inteiros deve ser considerada segura, mas no momento não há exceções.

Dito isto, aqui está o meu totalmente 100% não um sindicato Vector4. Como você pode ver, Dereffunciona para ocultar o código inseguro e faz com que você possa tratá-lo Vector4como uma estrutura ou uma matriz com base no contexto em que é usado. A transmutação também não é ideal, mas sinto que posso justificá-la nesse caso. Se você optar por fazer algo assim, também poderá implementar 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]
}