Optimize out type checking when implementing `MappedRc`

I'm working on MappedRc which can map Rc's value to another value without extra allocating and elimates the generic type of Rc.

Rc<T> + (fn(&T) -> U)  ========>  MappedRc<U>

This is my implementation:

It works fine except de-referencing needs type checking, where I commented in the playground. Is there a way to avoid this? Thanks.

EDIT: Is there a better way to achieve this without using Any?

If you can use nightly, there is downcast_ref_unchecked you could use, which is basically this line of code

Thanks for reply, but that needs unsafe Rust.

I don't think there's a way to unerase a type while avoiding both unsafe and Any.

I see, thanks.

In this case, you can avoid the need to unerase the source type U by moving where the type erasure happens— Package up both the Rc and the map function into a single type with a Uparameter, and then implement a trait that doesn’t mention U on that combined type. This can then be type-erased to just the trait interface, making U disappear:

NB: I renamed U to S (for “source”) here.

struct MappedRcImpl<S: ?Sized, F> {
    src: Rc<S>,
    map_fn: F,
}

impl<S: ?Sized, T: ?Sized, F: Fn(&S) -> &T> AsRef<T> for MappedRcImpl<S, F> {
    fn as_ref(&self) -> &T {
        (self.map_fn)(&self.src)
    }
}

pub struct MappedRc<'a, T: 'a + ?Sized>(Box<dyn 'a + AsRef<T>>);

impl<'a, T: 'a + ?Sized> MappedRc<'a, T> {
    pub fn new<S, F>(src: Rc<S>, map_fn: F) -> Self
    where
        S: ?Sized + 'a,
        F: Fn(&S) -> &T + 'a,
    {
        MappedRc(Box::new(MappedRcImpl { src, map_fn }))
    }
}

impl<T: ?Sized> Deref for MappedRc<'_, T> {
    type Target = T;
    fn deref(&self) -> &T {
        <_ as AsRef<T>>::as_ref(&*self.0)
    }
}

That works, but caused extra allocation here.

MappedRc ( Box::new( MappedRcImpl { src, map_fn } ) )

The allocation occurred because MappedRcImpl at least contains a Rc, which is not zero-sized. Thank you anyway.

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.