How to satisty the borrow-checker?

This is a simplified version of a piece of code I am writing. It's not production code, just code I'm writing to learn Rust, so any insights will be useful :slight_smile:

The idea of rem() is removing from A::m1 all elements that already exist in A::m2, but I cannot find a way to express it in Rust.

The following implementation does not work, and I could not figure out any implementation that satisfies the compiler.

What would you suggest to solve it?

struct A { 
    m1: std::vec::Vec<i32>,
    m2: std::vec::Vec<i32>
}

impl A {
    // Remove from m1 all elements that exist in m2
    fn rem(&mut self) {
        // pre-condition: m2 is sorted

        self.m1.retain(|e| match self.m2.binary_search(e) {
            Ok(_) => false,
            Err(_) => true
        }); 
    }   
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `self.m1` as mutable because it is also borrowed as immutable
  --> src/lib.rs:11:9
   |
11 |           self.m1.retain(|e| match self.m2.binary_search(e) {
   |           ^       ------ ---       ---- first borrow occurs due to use of `self` in closure
   |           |       |      |
   |           |       |      immutable borrow occurs here
   |  _________|       immutable borrow later used by call
   | |
12 | |             Ok(_) => false,
13 | |             Err(_) => true
14 | |         }); 
   | |__________^ 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.

You can fix it this way:

        let m2 = &mut self.m2;
        self.m1.retain(|e| match m2.binary_search(e) {
            Ok(_) => false,
            Err(_) => true
        }); 

The borrow checker is unhappy because your closure requires to borrow self as a whole, which is not possible because you also need to access self.m1 mutably at the same time. By creating a variable, we explain to the compiler that the closure only needs self.m2, not the whole self. And you're allowed to borrow self.m1 and self.m2 at the same time, so it compiles.

Note: you can use is_err() instead of a match here.

3 Likes

Also, RFC-2229 addresses this issue directly. It's accepted, but looks like work has not started on it, yet.