DazKins
DazKins

Reputation: 115

Convert Option<RefCell<T>> to Option<&T>

We know that it's easy to convert a value of type RefCell<T> to a value of type &T for use as a parameter in a function:

fn main() {
    let a: RefCell<i32> = RefCell::new(0);
    my_func(a.borrow().deref());
}

fn my_func(i: &i32) {}

In my scenario, the RefCells are stored in a HashMap, so they get obtained wrapped in an Option. I also want the function I pass them to have the notion of option, but I only want to pass the non-mutable reference, not the whole RefCell. We can achieve this like so:

fn main() {
    let a: Option<RefCell<i32>> = Some(RefCell::new(0));

    match a {
        Some(ref_cell) => my_func(Some(ref_cell.borrow().deref())),
        None => my_func(None)
    };
}

fn my_func(i: Option<&i32>) {}

This works, but in my specific scenario, my_func takes several of these Option<&T>s as a parameter, so doing it this way means the match just gets nested for each parameter and grows exponentially. It would therefore be helpful to have someway of doing this:

fn main() {
    let a: Option<RefCell<i32>> = Some(RefCell::new(0));

    let c = match a {
        Some(ref_cell) => Some(ref_cell.borrow().deref()), // won't compile as this borrow won't live long enough
        None => None
    };

    my_func(c);
}

fn my_func(i: Option<&i32>) {}

So essentially I want to be able to convert from Option<RefCell<T>> to Option<&T>. I feel like this should be possible somehow but I can't figure out a way to do it. I always run into some issue of performing the .borrow() on the RefCell but it not living long enough.

Upvotes: 2

Views: 596

Answers (1)

kmdreko
kmdreko

Reputation: 60062

You can do this using the methods on Option:

a.as_ref().map(RefCell::borrow).as_deref()
  • as_ref() is used to convert the Option<RefCell<_>> into a Option<&RefCell<_>> to avoid consuming it. If you already have a Option<&RefCell<_>> because you got it from hash_map.get() or similar, then you can skip this.
  • map(RefCell::borrow) is used to call .borrow() on the value if it exists. This will create a Option<Ref<'_, _>>.
  • as_deref() is the equivalent of calling .deref() on the value if it exists.

It is important to do it this way instead of trying to merge the .borrow() and .deref() in a single .map() call because this keeps the intermediate Ref<'_, _> value alive.

a.as_ref().map(|a| a.borrow().deref())
error[E0515]: cannot return reference to temporary value
 --> src/main.rs:8:24
  |
8 |     a.as_ref().map(|a| a.borrow().deref())
  |                        ----------^^^^^^^^
  |                        |
  |                        returns a reference to data owned by the current function
  |                        temporary value created here

Also, if have multiple parameters like this and you want to split them out into variables, be sure to take the Ref<'_, _> part on its own and use .as_deref() where you use it. Again this is needed to keep the intermediate Ref<'_, _> alive:

let a_ref = a.as_ref().map(RefCell::borrow);
let b_ref = b.as_ref().map(RefCell::borrow);
let c_ref = c.as_ref().map(RefCell::borrow);

f(a_ref.as_deref(), b_ref.as_deref(), c_ref.as_deref());

Upvotes: 7

Related Questions