By Drew


2015-04-01 22:02:00 8 Comments

I have a struct that has inner mutability.

use std::cell::RefCell;

struct MutableInterior {
    hide_me: i32,
    vec: Vec<i32>,
}
struct Foo {
    //although not used in this particular snippet,
    //the motivating problem uses interior mutability
    //via RefCell.
    interior: RefCell<MutableInterior>,
}

impl Foo {
    pub fn get_items(&self) -> &Vec<i32> {
        &self.interior.borrow().vec
    }
}

fn main() {
    let f = Foo {
        interior: RefCell::new(MutableInterior {
            vec: Vec::new(),
            hide_me: 2,
        }),
    };
    let borrowed_f = &f;
    let items = borrowed_f.get_items();
}

Produces the error:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:16:10
   |
16 |         &self.interior.borrow().vec
   |          ^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
17 |     }
   |     - temporary value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 15:5...
  --> src/main.rs:15:5
   |
15 | /     pub fn get_items(&self) -> &Vec<i32> {
16 | |         &self.interior.borrow().vec
17 | |     }
   | |_____^

The problem is that I can't have a function on Foo that returns a borrowed vec, because the borrowed vec is only valid for the lifetime of the Ref, but the Ref goes out of scope immediately.

I think the Ref must stick around because:

RefCell<T> uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can claim temporary, exclusive, mutable access to the inner value. Borrows for RefCell<T>s are tracked 'at runtime', unlike Rust's native reference types which are entirely tracked statically, at compile time. Because RefCell<T> borrows are dynamic it is possible to attempt to borrow a value that is already mutably borrowed; when this happens it results in task panic.

Now I could instead write a function like this that returns the entire interior:

pub fn get_mutable_interior(&self) -> std::cell::Ref<MutableInterior>;

However this potentially exposes fields (MutableInterior.hide_me in this example) that are really private implementation details to Foo.

Ideally I just want to expose the vec itself, potentially with a guard to implement the dynamic borrowing behavior. Then callers do not have to find out about hide_me.

3 comments

@Shepmaster 2018-07-14 17:49:33

Instead of creating a brand new type, you can use Ref::map (since Rust 1.8). This has the same result as Levans' existing answer:

use std::cell::Ref;

impl Foo {
    pub fn get_items(&self) -> Ref<'_, Vec<i32>> {
        Ref::map(self.interior.borrow(), |mi| &mi.vec)
    }
}

You can also use new features like impl Trait to hide the Ref from the API:

use std::cell::Ref;
use std::ops::Deref;

impl Foo {
    pub fn get_items(&self) -> impl Deref<Target = Vec<i32>> + '_ {
        Ref::map(self.interior.borrow(), |mi| &mi.vec)
    }
}

@DanielV 2019-10-07 21:52:23

What about instead of get_item, if you were implementing the std::ops::Index<> trait, which requires you to return &Self::Output . Returning a std::cell::Ref, as far as I have been able to figure out, won't satisfy the trait requirement. Is there a way to do interior mutability with for that trait?

@DanielV 2019-10-07 22:05:36

Actually I found a way to do it with UnsafeCell, so I think maybe that will be good enough.

@Shepmaster 2019-10-08 01:38:22

@DanielV Implementing Index trait to return a value that is not a reference. I wouldn't trust the UnsafeCell implementation because it's quite likely that it introduces memory unsafety.

@Levans 2015-04-01 22:19:32

You can create a new struct similar to the Ref<'a,T> guard returned by RefCell::borrow(), in order to wrap this Ref and avoid having it going out of scope, like this:

use std::cell::Ref;

struct FooGuard<'a> {
    guard: Ref<'a, MutableInterior>,
}

then, you can implement the Deref trait for it, so that it can be used as if it was a &Vec<i32>:

use std::ops::Deref;

impl<'b> Deref for FooGuard<'b> {
    type Target = Vec<i32>;

    fn deref(&self) -> &Vec<i32> {
        &self.guard.vec
    }
}

after that, update your get_items() method to return a FooGuard instance:

impl Foo {
    pub fn get_items(&self) -> FooGuard {
        FooGuard {
            guard: self.interior.borrow(),
        }
    }
}

and Deref does the magic:

fn main() {
    let f = Foo {
        interior: RefCell::new(MutableInterior {
            vec: Vec::new(),
            hide_me: 2,
        }),
    };
    let borrowed_f = &f;
    let items = borrowed_f.get_items();
    let v: &Vec<i32> = &items;
}

@norcalli 2015-04-02 08:37:08

Is this the only/idiomatic way to do this? Seems like a bit of trouble... Though I suppose instead of a getItems() method, you could borrow the internals in a block directly where it would then go out of scope (or something...)

@Levans 2015-04-02 08:40:43

@Norcalli In the specific case of RefCell, the object needs to be notified when the reference goes out of scope (that's what the destructor of Ref does). Here, we need to preserve this behavior (the error of the OP was due to the Ref instance being dropped too early), and thus encapsulate it.

@Francis Gagné 2018-07-15 06:08:20

You can wrap the Vec in an Rc.

use std::cell::RefCell;
use std::rc::Rc;

struct MutableInterior {
    hide_me: i32,
    vec: Rc<Vec<i32>>,
}
struct Foo {
    interior: RefCell<MutableInterior>,
}

impl Foo {
    pub fn get_items(&self) -> Rc<Vec<i32>> {
        self.interior.borrow().vec.clone() // clones the Rc, not the Vec
    }
}

fn main() {
    let f = Foo {
        interior: RefCell::new(MutableInterior {
            vec: Rc::new(Vec::new()),
            hide_me: 2,
        }),
    };
    let borrowed_f = &f;
    let items = borrowed_f.get_items();
}

When you need to mutate the Vec, use Rc::make_mut to obtain a mutable reference to the Vec. If there are still other Rcs referring to the Vec, make_mut will dissociate the Rc from the other Rcs, clone the Vec and update itself to refer to that new Vec, then give you a mutable reference to it. This ensures that the value in the other Rcs doesn't suddenly change (because Rc by itself doesn't provide interior mutability).

Related Questions

Sponsored Content

2 Answered Questions

1 Answered Questions

[SOLVED] Temporary Lifetime in Arc in Rust

  • 2019-01-28 12:02:44
  • Websterix
  • 92 View
  • 1 Score
  • 1 Answer
  • Tags:   rust lifetime

1 Answered Questions

1 Answered Questions

[SOLVED] Factory method: instance does not live long enough

  • 2015-05-02 20:24:30
  • W.K.S
  • 598 View
  • 2 Score
  • 1 Answer
  • Tags:   rust

1 Answered Questions

[SOLVED] Lifetime constraints to model scoped garbage collection

1 Answered Questions

[SOLVED] Encapsulating sequentially initialized state with self-references in Rust struct

  • 2016-09-12 00:50:32
  • Daniel S.
  • 500 View
  • 11 Score
  • 1 Answer
  • Tags:   rust

2 Answered Questions

[SOLVED] How to capture self consuming variable in a struct?

  • 2016-10-05 18:34:48
  • Tommi Komulainen
  • 280 View
  • 2 Score
  • 2 Answer
  • Tags:   struct rust

Sponsored Content