Search code examples
rustwrapperunsaferaw-pointer

How to cast a &T to a &Wrapper(T)?


I have this snippet of code showcasing a simplification of my problem.

struct Wrapper1(u32);

impl Wrapper1 {
    fn inner(&self) -> &u32 {
        &self.0
    }
}

struct Wrapper2(u32);

struct Wrapper3(Wrapper1);

trait GetInner {
    fn inner(&self) -> &Wrapper2;
}

impl GetInner for Wrapper3 {
    fn inner(&self) -> &Wrapper2 {
        let raw_pointer = self.0.inner() as *const u32 as *const Wrapper2;
        unsafe {  &*raw_pointer }
    }
}

I have to rely on unsafe because I cannot just do this:

impl GetInner for Wrapper3 {
    fn inner(&self) -> &Wrapper2   
        &Wrapper2(*self.0.inner())
    }
}

Is it sound? Somebody in my team raised concern about alignment. Somebody else mention we have to use #[repr(transparent)] on the wrappers types in order to make it more safe.


Solution

  • You need Wrapper2 to be #[repr(transparent)] or #[repr(C)]. Layout of #[repr(Rust) structs is not guaranteed.

    However, if you do that, you don't need unsafe anymore - you can use bytemuck:

    struct Wrapper1(u32);
    
    impl Wrapper1 {
        fn inner(&self) -> &u32 {
            &self.0
        }
    }
    
    #[derive(bytemuck::TransparentWrapper)]
    #[repr(transparent)]
    struct Wrapper2(u32);
    
    struct Wrapper3(Wrapper1);
    
    trait GetInner {
        fn inner(&self) -> &Wrapper2;
    }
    
    impl GetInner for Wrapper3 {
        fn inner(&self) -> &Wrapper2 {
            bytemuck::TransparentWrapper::wrap_ref(self.0.inner())
        }
    }