1

I have a RefCell in my structure which I want to access and modify. However, the only way I can access this RefCell is using a closure. Hence, I'd like to return a mutable reference (RefMut) from the closure which I can use and modify from the enclosing scope.

{
    let mut state_ref: RefMut<_> = self.dialog.call_on_name("dirview", |view: &mut Canvas<RefCell<FileDialogState>>| {
        let state: &RefCell<FileDialogState> = view.state_mut();
        state.borrow_mut()
    }).unwrap();

    state_ref.foo = bar;

    // Mutable reference to RefCell should cease to exist here
}

However, the compiler complains that the RefMut I am returning outlives the lifetime of the closure where I create it.

Despite this, I've seen code which does something similar to what I'm trying to achieve -- for example here:

impl Backend {
    pub fn init() -> ... {
        let stdout = RefCell::new(BufWriter::new(io::stdout()));
        ...
    }

    fn stdout_mut(&self) -> RefMut<BufWriter<Stdout>> {
        self.stdout.borrow_mut()
    }

What is the difference between these two use cases? What am I missing to be able to return the RefMut reference?

Albert Tomanek
  • 387
  • 4
  • 13

1 Answers1

0

RefCell checks shared mutable access dynamically, but it does not manage ownership of the entire cell dynamically (that's the job of things like Rc). A Ref or RefMut is only valid as long as the underlying RefCell exists, and that's why you can't return the RefMut from this closure: there's no guarantee in the structure of that code that the RefCell will continue existing. (There could be, if the type of .state_mut() specified a sufficiently long lifetime for the returned reference, but evidently it does not.)

In the second case, stdout_mut has elided lifetimes which link the lifetime parameter of the returned RefMut to the lifetime of &self, which provides the necessary guarantee. (If you change lint settings to #[warn(elided_lifetimes_in_paths)] then you'll get a warning whenever a lifetime parameter in a user-defined type is elided, which can help make this clearer.)

To solve your problem in the first case, you can:

  • Change your structure to have Rc<RefCell<_>> instead of just RefCell<_>. Then you can return Rc::clone(view.state_mut()) from the closure, and borrow it afterward.

  • Borrow and make your changes within the closure.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108