Cannot borrow *self as mutable because it is also borrowed as immutable

In the following example code, I get the error of "Cannot borrow *self as mutable" on the commented line.

I've tried different variations of mut, not mut, borrow(), borrow_mut(), but can't seem to get around it.

Gotta run but will followup later... help is greatly appreciated. Thanks!

struct Foo {
  context: Rc<RefCell<Context>>
}

impl Foo {
  fn tick(self:&mut Self) {
    //This is the problem line:
    self.render(&self.context.borrow());
  }
}

trait Render {
    fn render(&mut self, context:&Context);
}

impl Render for Foo {
  fn render(self:&mut Self, context:&Context) {
  ...
  }
}
1 Like

&mut means exclusive mutable access.

&mut self in a method call means it requires exclusive access to all of the object, all its fields, including self.context.

When you borrow &Context you're creating a paradox:

  • & is a shared immutable access, so you're promising there is no way for Context to change for as long as &Context exists
  • But you're promising excursive access to self.context which (as far as the borrow checker can track) allows it to be mutated.

So self.context and the &Context argument can never refer to the same object.

Rust's borrow checker unfortunately has no way to express "call a method giving it access to some, but not all, fields".

Some options you have:

  • You could pass &Context to tick as well, and not have self.context at all.
  • Keep using self.context (or pass RefCell<Context> as the argument) and re-borrow each time.
  • Refactor self into two smaller objects, one with the context, used as read-only, and one without, used as mutable.
  • Use &self with &Context argument, and interior mutability for other fields too.
6 Likes

Whoa - another great bullet list :smiley:

Feels like this is an essential sort of thing that pops up fairly often in real-world code?

2 Likes

I understand the explanation; it was clear and complete.

To help me integrate the rule in my thinking, how can I think differently about the following code which generates the error of this topic.

 set_header(&mut self) -> &'a mut Scanner {
     self.header = get_header(&self.filepath).ok();
     self
}

In this code I see a sequence. In words, where "I" = "struct":

I am lending a reference to one of my values, to set another one of my values.
Once the value is updated, I return a mutable reference to myself.

The sequence of events:

  1. lend out a value of mine
  2. resolve to completion the computation required to set another one of my values
  3. with the computation now complete, I'm no longer lending out anything
  4. it's now safe to share a mutable reference.

Is my thinking about an imperative sequence not right? In my thinking, the Rust account for the struct as a whole should not be a limiting factor.

Thanks to anyone that can help me reframe how I'm thinking about this!

- E

live update

Having articulated the question, I see an error in my thinking. The key:

self.header = get_header(&self.filepath).ok();

The value of self.header relies on a computation, that relies on a borrowed value. In itself, that's ok. Once the struct is shared as mutable, the computation with that mutable access could change self.filepath. That change would change the computation of self.header; not expected/corrupt. Because the referential integrity can no longer be relied upon => compiler error.

However, it still does not fully get me out of the "sequence"; intuitively there should be a way to contract in the timing... Lifetimes.

I don't have enough context to recreate the error you're getting.

Opening a new topic would be better than reviving an old one such as this topic.