Get out of the value from Ref

I have struct Data, which has a field n: RefCell. And I have a trait GetVec as following:

use std::cell::{RefCell, Ref};
use std::collections::HashMap;
use std::ops::Deref;

struct Data {
    n: RefCell<HashMap<i32, Vec<i32>>>
}

trait GetVec {
    fn get_vec(&self, i: i32) -> Ref<Vec<i32>>;
}

impl GetVec for Data {
    fn get_vec(&self, i: i32) -> Ref<Vec<i32>>  {
        self.n.borrow_mut().insert(1, vec![1,2,3]);
        let data = self.n.borrow();
        Ref::map(data, |t| &t[&i])
    }
}

But the method get_vec from GetVec can only return Ref<Vec<i32>>. What should I do if I need to return &'_ Vec<i32>:

trait GetVec {
    fn get_vec(&self, i: i32) -> &'_ Vec<i32>;
}

impl GetVec for Data {
    fn get_vec(&self, i: i32) -> &'_ Vec<i32>  {
        self.n.borrow_mut().insert(1, vec![1,2,3]);
        let data = self.n.borrow();
        Ref::map(data, |t| &t[&i])
    }
}

You can't do that. Doing that defeats the purpose of using RefCell.

If you are not using trait objects, you can change your trait's definition to this:


trait GetVec {
    type VecRef<'a>: Deref<Target = Vec<i32>>
        where Self: 'a;
    fn get_vec<'a>(&'a self, i: i32) -> Self::VecRef<'a>;
}

Althought I doubt whether this is what you want.

2 Likes

I find a indirect way to do this, which seem a little verbose:

use std::cell::{RefCell, Ref};
use std::collections::HashMap;
use std::ops::Deref;

struct Data {
    n: RefCell<HashMap<i32, Vec<i32>>>
}

trait GetVec {
    fn get_vec(&self, i: i32) -> GetGuard<'_, Vec<i32>>;
}

impl GetVec for Data {
    fn get_vec(&self, i: i32) -> GetGuard<'_, Vec<i32>>  {
        self.n.borrow_mut().insert(1, vec![1,2,3]);
        let data = self.n.borrow();
        GetGuard { value: (Ref::map(data, |t| &t[&i])) }
    }
}

struct GetGuard<'a, T> {
    value: Ref<'a, T>
}

impl<'a, T> Deref for GetGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.value
    }
}

fn test_data(x: &Vec<i32>) -> Vec<i32> {
    x.iter().map(|a| a + 1).collect()
}

fn main() {
    let mut hm = HashMap::new();
    hm.insert(1, vec![1i32,2,3]);
    let data = Data { n: RefCell::new(hm) };
    let b = data.get_vec(1);
    test_data(&b)
}

Ref<'a, T> already implements Deref<Target = T>, so you can use it directly instead of GetGuard.

3 Likes

Just think about it – this isn't something that should be possible.

The RefCell needs to keep track of whether it is borrowed (and if so, whether it is borrowed mutably or immutably). A plain reference can't do that. It can't call back to its owner, it has no knowledge whatsoever as to any wrapper types it might point inside.

This is exactly why Ref(Mut) exists: it's an RAII guard that signals to its originating RefCell upon being dropped that the borrow is ended. If you could obtain a borrow that is independent of the lifetime of any Ref(Mut)s, then it would be unsound. You could have some outstanding immutable borrows, drop all Refs, and borrow the inner value mutably, creating an aliasing &mut, which is insta-UB.

Thus, any returned reference must have its lifetime tied to the existence of Ref(Mut), and so the only way to get such a (short-lived) reference is to go through the guard object itself, because that's how the compiler can ensure, by way of lifetime annotations, that it doesn't outlive the guard object.

2 Likes

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.