edubrunaldi
edubrunaldi

Reputation: 13

Different approach to call a mutable method inside a for iterator

I'm trying to call a mutable method inside a immutable scope with gives me: error[E0502].

I'm already understand why this error occurs but I'm struggle on how to fix it or how to do a different approach that works.

Here a MCVE

 struct A {
    list: Vec<i8>,
    has_three: bool,
}

impl A {
    pub fn new() -> Self {
        Self {
            list: vec![1,2,3],
            has_three: false,
        }
    }
    pub fn mutable_method(&mut self) {
        for item in self.list.iter() {
            self.mutable_method2(item);
        }
    }
    
    fn mutable_method2(&mut self, item: &i8) {
        let b: i8 = 3;
        if item == &b {
            self.has_three = true;
        }
    }
}


fn main() {
    let mut a = A::new();
    a.mutable_method();
}

And the Error received:

Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:15:13
   |
14 |         for item in self.list.iter() {
   |                     ----------------
   |                     |
   |                     immutable borrow occurs here
   |                     immutable borrow later used here
15 |             self.mutable_method2(item);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Upvotes: 1

Views: 112

Answers (1)

L. Riemer
L. Riemer

Reputation: 654

You asked for another approach. If I correctly assume that your goal is to have has_three be true if any entry is 3, that's much more easily done this way:

pub fn mutable_method(&mut self) {
    self.has_three = self.list.iter().any(|&x| x == 3);
}

By the way, you need to make sure mutable_method is actually called appropriately, otherwise you will end up in logically invalid states. That's not good practice. Consider extracting this to the constructing function already.

Some background

The underlying problem is that your initial approach wants to borrow self mutably, while already borrowing it immutably. However, logically, your code is not obviously invalid, as you only borrow a part of the struct mutably that you do not also borrow immutably. This information is lost though.

We make the safety explicit by factoring into two implicit operations,

let tmp = self.list.iter().any(|&x| x == 3);
self.has_three = tmp;

which both operate on the struct in a 'clear fashion', either mutably or immutably. That is how you can approach such problems.

Upvotes: 1

Related Questions