Search code examples
rustownershipborrowingrefcell

Return Ref to something inside of Rc<RefCell<>> without Ref::map


Working code first:

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

struct ValueHolder {
    value: i32
}

fn give_value(wrapped: &Rc<RefCell<ValueHolder>>) -> Ref<i32> {
    Ref::map(
        (**wrapped).borrow(),
        |borrowed| { &(*borrowed).value },
    )
}

fn main() {
    println!("Our value: {}", *give_value(
        &Rc::new(RefCell::new(ValueHolder { value: 1337 }))
    ));
}

The relevant part is the give_value function.

I want to return a Ref to something inside a Rc<RefCell<>>, in this case a value inside of a struct.

That works just fine, but since I just started to learn Rust, I wonder, how I could achieve the same thing without using Ref::map.

The "naive" approach:

fn give_value(wrapped: &Rc<RefCell<ValueHolder>>) -> &i32 {
    &(*(**wrapped).borrow()).value
}

fails for obvious reasons:

error[E0515]: cannot return value referencing temporary value
 --> src/bin/rust_example.rs:9:5
  |
9 |     &(*(**wrapped).borrow()).value
  |     ^^^--------------------^^^^^^^
  |     |  |
  |     |  temporary value created here
  |     returns a value referencing data owned by the current function

So my question is: How can I recreate what the Ref::map function does by myself?


Solution

  • You can't. RefCell requires that Ref be used whenever the value is used, so it knows when it should allow a borrow. When the Ref gets dropped, it signals to the RefCell that it can decrease the borrow count, and if the borrow count is 0, then a mutable borrow can happen. Being able to obtain a reference to the internal data that doesn't borrow the Ref (note that a reference you can return from a function cannot borrow the Ref otherwise you'd be referencing a temporary) would be problematic, because then the Ref could get dropped and the RefCell thinks that it can lend out a mutable borrow, but there's still an immutable borrow to the data. The source code for Ref::map is:

    pub fn map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
    where
        F: FnOnce(&T) -> &U,
    {
        Ref { value: f(orig.value), borrow: orig.borrow }
    }
    

    which uses private fields that you can't access. So, no, you cannot recreate Ref::map yourself.