RefCell<RefCell<T>> -> Ref<T>?

fn foo<T>(x: &std::cell::RefCell<T>) -> std::cell::Ref<T> {
    x.borrow()
}

fn foo2<T>(x: &std::cell::RefCell<std::cell::RefCell<T>>) -> std::cell::Ref<T> {
    x.borrow().borrow()
}

foo compiles. foo2 refuses to compile. Why?

Is it possible to get foo2 to compile by adding some 'a, 'b, 'c to the type signature ?

It fails because the return of the second borrow borrows from the Ref returned from the first borrow. Unfortunately Ref::map requires you to return a reference, so you can't just make a Ref<Ref<T>>.

Basically you're trying to return an item along with what its borrowed from.

The next thing I tried is this:

struct MyRef<'a, T> {
    inner: Ref<'a, RefCell<T>>,
}
impl<'a, T> Deref for MyRef<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        // ... ?
    }
}

fn foo2<T>(x: &RefCell<RefCell<T>>) -> Ref<Ref<T>> {
    MyRef { inner: x.borrow() }
}

but this does not work as the Ref from the inner needs to be stored somewhere. This seems to be your best bet:

struct MyRef<'a, T> {
    inner: Ref<'a, RefCell<T>>,
}
impl<'a, T> MyRef<'a, T> {
    fn borrow(&self) -> Ref<'_, T> {
        self.inner.borrow()
    }
}

fn foo2<T>(x: &RefCell<RefCell<T>>) -> MyRef<T> {
    MyRef { inner: x.borrow() }
}

even though it requires two calls, e.g. foo2(val).borrow() to get to a single Ref<T>.

Unfortunately these two calls are required to correctly handle both Refs being dropped without a self-referential type.

1 Like

@alice: Thank you for the detailed reply. It sounds like we can't get foo and foo2 to have the same return Type? Is that correct?

I'm not sure how to rigorously state: "creating an enum with two arms, one for foo, one for foo2, doesn't count."

  1. This code is trying to acquire read access of two different RefCells. One is inside of the another doesn't matter much.

  2. Since it's acquiring two of them, it needs to store two Ref guards which releases the access on drop. This is why Ref<T> cannot works.

  3. Ref<T> doesn't owns the T - it only borrows it. So Ref<Ref<T>> doesn't own the inner Ref<T>, then who can owns it after the function returns?

1 Like

pub enum Blah<T> {
    A(RefCell<T>),
    B(RefCell<Box<Blah<T>>>),
}

pub enum MyRerf<T> {
    A(Ref<T>),
    // what else to add here?
}


impl <T> Blah<T> {
    pub fn get_my_ref(&self) -> MyRef<T>

}

impl<T> MyRerf<T> {
    pub fn get_ref(&self) -> &T {
        match self {
            MyRerf::A(r) => &r,
            _ => todo!(),
        }
    }
}

Is it possible to (1) modify MyRef, (2) add some lifetime parameters, and get it so that the above code can compile?

Blah is either a RefCell or nested RefCells. Then, I want to be able to construct an object MyRef, and I want MyRef to be able to get a &T from.

Nested RefCells are a pain to work with, and I suggest you restructure your code to avoid it. Why do you need nested RefCell?

One solution is to not hand out references, and instead do this:

impl <T> Blah<T> {
    pub fn with_ref<R>(&self, f: impl FnOnce(&T) -> R) -> R {
        match self {
            Self::A(val) => f(&val.borrow()),
            Self::B(rec) => rec.borrow().with_ref(f),
        }
    }
}

But this has it's own problems, most notably it is verbose to work with and hard to read

One way to restructure you code is to change Blah into this

struct Blahs<T> {
    values: RefCell<Vec<BlahInner<T>>>
}

enum BlahInner<T> {
    Value(T),
    Next(usize)
}

You can then implement get_ref like so playground

I'm implementing something similar to Jane Street Tech Blog - Breaking down FRP 's

val join: 'a signal signal -> 'a signal

'a signal ends up being involving a RefCell<T>

The 'a signal signal ends up being a nested RefCell

FRP is all about avoiding shared mutable state. Why did you think you need a RefCell for it?

Using FRP is all about avoiding shared mutable state.

I'm implementing a FRP engine, the par that evaluates the "pure, functional" abstraction.

Thank you for the detailed solution. This solves the problem I stated but not the problem I wanted to state. I need to figure out how to better phrase my requirements.

You could return a tuple with both the inner and the outer ref, so the outer one doesn't die too early, which is your problem.

Unfortunately such a tuple is not possible because one would be a borrow of the other, making it a self-referential type.

For what is worth, the following works:

use ::core::cell::{RefCell, RefMut};

fn foo<T> (x: &'_ RefCell<T>) -> RefMut<T>
{
    x.borrow_mut()
}

fn foo2<T> (x: &'_ RefCell<RefCell<T>>) -> RefMut<T>
{
    RefMut::map(x.borrow_mut(), RefCell::get_mut)
}
1 Like

I'm not sure I can make it work using RefMut (i.e. might be borrowing it twice). However, the fact that this works at all is intriguing.

1 Like

Because the RefCell::get_mut doesn't acquire any lock. It's a simple projection from unique reference to unique reference, which is guaranteed to be succeed and sound.

Ahh, right. The address of the pointer would change. Thanks for pointing that out. I was somehow thinking about both pointers being independent, which isn't the case.

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