Is this an acceptable use case for Deref?

I'm trying to find the best way to write a VectorN with the least code repetition possible and have easy access to both its fields x, y, z, w, ... and its slice representation. Here's what I did:

#[repr(C)]
#[derive(Clone, Copy)]
pub struct Vector<T: Copy, const N: usize>([T; N]);
  
  
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Vector4View<T>{
    pub x: T,
    pub y: T,
    pub z: T,
    pub w: T,
}
  
  
impl<T: Copy> Deref for Vector<T, 4>  {
    type Target = Vector4View<T>;
  
    fn deref(&self) -> &Self::Target {
        unsafe { mem::transmute(self) }
    }
}

Now I can write most methods for all of VectorN's at the cost of some unsafe code and a non-standard Deref usage.

Question: Is this safe or sound? Is this a bad idea?

You should use a pointer cast instead of transmuting the reference.

5 Likes

Maybe it should also be #[repr(transparent)] instead of #[repr(C)]? Though I guess it results in the same in the given case.

For the second declaration, repr(transparent) isn't possible. I think for the first one it might make a difference for N = 0.

Ooops, sorry I missed that.

Do you mean like this?

fn deref(&self) -> &Self::Target {
    unsafe { &*(self as *const Vector<T, 4>).cast::<Self::Target>() }
}

#[repr(transparent)] actually errors on [T; 0], but works just like #[repr(C)] for [T; N] when N is 0

1 Like

Oddly, it works with [u8; 0] (Playground).

File a bug. It looks like this was missed when in 1.55 we made repr(transparent) require at most one thing that's not a 1-ZST, rather than the exactly one thing that's not a 1-ZST.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.