Cannot borrow as mutable more than once when calling &'a mut self method

#1

This is my second day with Rust so please bear with me.
I’m not sure why I get a “cannot borrow” here:

struct A<'a> {
    b: &'a mut B,
    v: Vec<&'a B>,
}

struct B {
}


impl<'a> A<'a> {
    fn x(&'a mut self) {
        self.v.push(self.b);
    }
}

fn main() {
    let mut b = B { };
    let b = &mut b;
    let mut a = A {
        b,
        v: vec![],
    };
    a.x();
    a.x();
}

The error I get:

error[E0499]: cannot borrow `a` as mutable more than once at a time
  --> demo.rs:24:5
   |
23 |     a.x();
   |     - first mutable borrow occurs here
24 |     a.x();
   |     ^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

I’m a bit puzzled. Why would a.x() be a borrow at all, I thought let mut a would be the only borrow here.

0 Likes

#2

The x method takes &'a mut self, so it must borrow the A struct for the same lifetime as the references stored inside of the struct. This must be at least as long as the struct’s own lifetime, since you can’t store short-lived references in a long-lived struct:

fn x(&'a mut self)

As you’ve seen, taking an unique reference to a struct and storing it within that struct effectively locks that struct for its entire lifetime, preventing any other use of the struct, ever.

One way around this is to use a type like RefCell so that x can take a shared reference instead of a unique reference:

use std::cell::RefCell;

struct A<'a> {
    b: &'a mut B,
    v: RefCell<Vec<&'a B>>,
}

impl<'a> A<'a> {
    fn x(&'a self) {
        self.v.borrow_mut().push(self.b);
    }
}
1 Like

#3

I’ll add that, in general, circular references and self-referential structures are a tricky case for Rust’s type system, and many Rust programming techniques are about avoiding such structures completely.

1 Like

#4

Thank you very much! I’ll try to rethink the way I structured it. But it was nevertheless important to understand the problem here (to the extent I actually understand it, that is :slight_smile:).

0 Likes