Reference to wrapper from reference

Given a wrapper type, for example

struct Wrapper(Rc<str>);

I wonder whether there is a way to obtain &Wrapper from &Rc<str> at zero cost.

A use case would be HashMap::get. Of course with Rc it does not matter that much, as cloning Rc is cheap.

If you mark the type as #[repr(transparent)], then it is equivalent to its single field, and can be transmuted soundly.

#[repr(transparent)]
struct Wrapper(Rc<str>);

impl Wrapper {
    pub fn cast(ptr: &Rc<str>) -> &Wrapper {
        unsafe { &*(ptr as *const Rc<str> as *const Wrapper) }
    }
}

playground

3 Likes

OK, that's nice enough, and as becomes apparent, a factorization is feasible by trait and macro. Following the terminology of TransparentNewtype from core_extensions, one obtains:

mod transparent {
    pub trait Transparent {
        type Inner;
        #[inline]
        fn convert_ref_from(ptr: &Self::Inner) -> &Self
        where Self: Sized
        {
            unsafe { &*(ptr as *const Self::Inner as *const Self) }
        }
    }
    #[macro_export]
    macro_rules! transparent {
        (struct $wrapper:ident($typ:ty);) => {
            #[repr(transparent)]
            struct $wrapper($typ);
            impl Transparent for $wrapper {type Inner = $typ;}
        };
        (pub struct $wrapper:ident($typ:ty);) => {
            #[repr(transparent)]
            pub struct $wrapper($typ);
            impl Transparent for $wrapper {type Inner = $typ;}
        }
    }
}

Just a note. it should be unsafe to implement the trait, otherwise your code allows safe code to observe UB.

mod transparent {
    pub unsafe trait Transparent {
        type Inner;
        #[inline]
        fn convert_ref_from(ptr: &Self::Inner) -> &Self
        where Self: Sized
        {
            unsafe { &*(ptr as *const Self::Inner as *const Self) }
        }
    }
    #[macro_export]
    macro_rules! transparent {
        ($v:vis struct $wrapper:ident($typ:ty);) => {
            #[repr(transparent)]
            $v struct $wrapper($typ);
            unsafe impl Transparent for $wrapper {type Inner = $typ;}
        };
    }
}

Or better yet, don't use a trait for this

#[repr(transparent)]
pub struct Wrapper<T: ?Sized>(T);

impl<T: ?Sized> Wrapper<T> {
    pub fn from_ref(t_ref: &T) -> &Self {
        unsafe { &*(ptr as *const T as *const Self) }
    }
}
// use like this,
type MyWrapper = Wrapper<MyType>;

Unfortunately, the Wrapper<T> approach conflicts with orphan rules.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.