Is there any function to map RefCell<T> to RefCell<U>?

#1

In writing application in Rust, I sometimes need to wrap an object of type T into Rc<RefCell<T>> to share while keeping it mutable.

Now, in addition, I sometimes need to access the inner value of T which is of type U. What I want to do is share only the inner value because sharing T is too wide. Let’s suppose the T is like struct T { inner: U }.

Since the result of borrow and borrow_mut both have map function since 1.8.0. It is natural for me that RefCell itself should have map function which makes me enable to access the inner value when calling borrow or borrow_mut.

I am finding a functionality like this. Any ideas?

#3

I’m not sure what kind of signature you have in mind? Surely you’re not just thinking of

impl<T> RefCell<T> {
    fn map(self, f: impl FnOnce(T) -> U) -> RefCell<U> {
        RefCell::new(f(self.into_inner()))
    }
}

because there’s no way you would be able to apply that through an Rc.

1 Like
#4

The fundamental difference between Ref and RefCell is that Ref<'a, T> is a pointer to T, but RefCell<T> is not a pointer to T; it’s just a T (plus an extra borrow count). You can’t have two RefCells that own the same thing because RefCell is not a means of shared ownership, only shared mutability. (Which RefCell would be “allowed” to destroy the T?)

For shared ownership you need to use Rc<T>, but Rc also does not currently have a means of mapping Rc<T> to Rc<U> because it stores the T in the same allocation as the reference counts. (IIRC shared_ptr in C++ uses two allocations, and so this kind of mapping is possible there. The tradeoff being that every shared_ptr is two pointers instead of only one.)

1 Like
#5

Thinking more for a while, it may be something like this (pseudo)

struct Map<T, F> {
    x: Rc<RefCell<T>>,
    f: F(T->U)
}
impl Map {
    fn borrow(&self) -> Ref<U>
    fn borrow_mut(&self) -> RefMut<U>
}

but this is achievable if RefCell by itself is able to map because there is a deref from &Rc to &T.

1 Like
#6

Notice that a fn(T) -> U does not admit an fn(&T) -> &U or similar. It would have to be something like:

struct Map<T, FR, FM>
where
    FR: Fn(&T) -> &U,
    FM: Fn(&mut T) -> &mut U,
{
    x: Rc<RefCell<T>>,
    f_ref: FR,
    f_mut: FM,
}

meaning the user would need to provide two closures, and they would have difficulty naming the type due to all the Fns. All things considered, it’s probably just easier to implement your own wrapper type that performs the projections:

// e.g. to get the 'thumb' field of a struct called 'Hand'
struct ThumbCell {
    hand: Rc<RefCell<Hand>>,
}

impl ThumbCell {
    fn borrow(&self) -> Ref<'_, Thumb>
    { Ref::map(self.hand.borrow(), |h| &h.thumb) }

    fn borrow_mut(&mut self) -> RefMut<'_, Thumb>
    { RefMut::map(self.hand.borrow_mut(), |h| &mut h.thumb) }
}

That should work fine if there’s only one field you need to do this for. If you have many, many such projections on the same type, then this might be more scalable:

// e.g. to get any field of a struct called `hand`
struct Map<U> {
    hand: Rc<RefCell<Hand>>,
    // fn(T) -> U is strictly less powerful than `Fn(T) -> U`, but easier to name
    f_ref: fn(&Hand) -> &U,
    f_mut: fn(&mut Hand) -> &mut U,
}

// or to get any field of any struct
struct Map<T, U> {
    hand: Rc<RefCell<T>>,
    f_ref: fn(&T) -> &U,
    f_mut: fn(&mut T) -> &mut U,
}

In any case, the fact that it requires two closures makes it too awkward for the standard library IMO.

1 Like
#7

@ExpHP That is exactly what I want and as an extension I find we can write a generalized implementation if T: DerefMut<Target=U> is given. Here is the PoC code:

use std::rc::Rc;
use std::cell::{RefCell, Ref, RefMut};
use std::ops::DerefMut;

trait Share<T> {
    fn borrow(&self) -> Ref<'_, T>;
    fn borrow_mut(&self) -> RefMut<'_, T>;
}

use std::marker::PhantomData;
impl <T> Share<T> for Rc<RefCell<T>> {
    fn borrow(&self) -> Ref<'_, T> {
        RefCell::borrow(self)
    }
    fn borrow_mut(&self) -> RefMut<'_, T> {
        RefCell::borrow_mut(self)
    }
}
struct Map<S, T> {
    x: S,
    phantom: PhantomData<T>,
}
impl <S, T> Map<S, T> {
    fn new(x: S) -> Self {
        Self { x, phantom: PhantomData }
    }
}
impl <T, U, S: Share<T>> Share<U> for Map<S, T> where T: DerefMut<Target=U> {
    fn borrow(&self) -> Ref<'_, U> {
        Ref::map(self.x.borrow(), T::deref)
    }
    fn borrow_mut(&self) -> RefMut<'_, U> {
        RefMut::map(self.x.borrow_mut(), T::deref_mut)
    }
}

#[cfg(test)]
mod tests {
    use super::Share;
    use std::rc::Rc;
    use std::cell::RefCell;
    use std::ops::{Deref, DerefMut};
    struct Hand {
        thumb: i32,
    }
    impl Deref for Hand {
        type Target = i32;
        fn deref(&self) -> &Self::Target {
            &self.thumb
        }
    }
    impl DerefMut for Hand {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.thumb
        }
    }
    #[test]
    fn test_map() {
        let hand = Hand { thumb: 5 };
        let raw = Rc::new(RefCell::new(hand));
        let map = super::Map::new(raw);

        assert_eq!(*map.borrow(), 5);
        *map.borrow_mut() -= 1;
        assert_eq!(*map.borrow(), 4);
    }
}

Rc<RefCell<T>> is very often seen in Rust code and I believe standard lib should have such abstraction for more easy of use.

#8

P.S. I finally invented a type like this, a type which is like () => RefMut<T>. And I already widely use this type in my project to solve my problem but much smells and should some ways exist not using such a awkward abstraction.

pub trait AsRefMut<T>: Clone {
    fn borrow_mut(&self) -> RefMut<T>;
    fn map<U>(self, f: fn(&mut T) -> &mut U) -> Map<Self,T,U> where Self: Sized {
        Map::new(self, f)
    }
}

impl <T> AsRefMut<T> for Rc<RefCell<T>> {
    fn borrow_mut(&self) -> RefMut<T> {
        RefCell::borrow_mut(self)
    }
}

pub struct Map<S,T,U> {
    orig: S,
    f: fn(&mut T) -> &mut U,
}
impl <S,T,U> Map<S,T,U> where S: AsRefMut<T> {
    pub fn new(orig: S, f: fn(&mut T) -> &mut U) -> Self {
        Self { orig, f }
    }
}
impl <S,T,U> AsRefMut<U> for Map<S,T,U> where S: AsRefMut<T> {
    fn borrow_mut(&self) -> RefMut<U> {
        RefMut::map(self.orig.borrow_mut(), self.f)
    }
}
impl <S,T,U> Clone for Map<S,T,U> where S: AsRefMut<T> {
    fn clone(&self) -> Self {
        Map::new(self.orig.clone(), self.f)
    }
}