Cannot borrow was immutable because it is also borrowed as mutable

I checked out similar topics but i failed to address the issue. Blow i construed a small example that emulates what i have in reality.

#[derive(Debug, Clone)]
struct Y {
    pub a: (usize, usize),
    pub b: f32,
}

#[derive(Debug, Clone)]
struct X {
    xx: Vec<Y>,
}

impl X {
    pub fn new() -> Self {
        X { xx: Vec::new() }
    }
    fn add(&mut self, x: Y) -> &mut Self {
        self.xx.push(x);
        self
    }
    fn pr(&self, x: &str) -> String {
        x.to_string()
    }
}

fn main() {
    // new X object
    let mut xx = X::new();
    // fill X
    xx.add(Y { a: (3, 7), b: 7.6 })
        .add(Y { a: (37, 8), b: 2.6 });
    // call mut since the for loop later does some changes
    // but it also calls other methods that need to be executed
    for _i in xx.xx.iter_mut() {
        println!("{:?}", xx.pr("cc"))
    }
}

Playground

Error naturally is:

Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `xx` as immutable because it is also borrowed as mutable
  --> src/main.rs:34:26
   |
33 |     for _i in xx.xx.iter_mut() {
   |               ----------------
   |               |
   |               mutable borrow occurs here
   |               mutable borrow later used here
34 |         println!("{:?}", xx.pr("cc"))
   |                          ^^ immutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to previous error

How do i bypass this issue? I need the object X to be mutable as the elements within the array are subject to change in my for loop but i also need the access to the methods within X that contain recipes on how this should be changed (changes depend on outside factors)

any advice ??
Thank you

Can you put those recipes in a different type? You can store them in a field of X and access the methods of the subfield? Then the borrow checker is able to use split borrows to access your recipes immutably while still being able to mutably iterate over the vector. Example:

#[derive(Debug, Clone)]
struct Z;

impl Z {
    fn pr(&self, x: &str) -> String {
        x.to_string()
    }
}

#[derive(Debug, Clone)]
struct Y {
    pub a: (usize, usize),
    pub b: f32,
}

#[derive(Debug, Clone)]
struct X {
    xx: Vec<Y>,
    z: Z,
}

impl X {
    pub fn new() -> Self {
        X { xx: Vec::new(), z: Z }
    }
    fn add(&mut self, x: Y) -> &mut Self {
        self.xx.push(x);
        self
    }
}

fn main() {
    // new X object
    let mut xx = X::new();
    // fill X
    xx.add(Y { a: (3, 7), b: 7.6 })
        .add(Y { a: (37, 8), b: 2.6 });
    // call mut since the for loop later does some changes
    // but it also calls other methods that need to be executed
    for _i in xx.xx.iter_mut() {
        println!("{:?}", xx.z.pr("cc"))
    }
}

Playground.

2 Likes

You can also do this

    for i in 0..xx.xx.len() {
        &mut xx.xx[i]; // use the elem before
        println!("{:?}", xx.pr("cc"));
        &mut xx.xx[i]; // or after
    }

No matter what solutions, you should never create mutable and imutable references at the same time (i.e. pointing to the same variable with their lifetimes overlapping), as the compiler denies.

3 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.