Cannot call method when borrowing mutable struct reference

In the following example code (Rust playground))

the borrow checker rejected the call to self.new_count(). It seems to me should not be an error. What is the proper way to fix such problem? . Thanks.

use std::collections::HashMap;

struct Foo {
    map: HashMap<String, String>,
    search_count: u64,
}

impl Foo {
    fn check_and_count(&mut self, k: &str) {
        match self.map.get_mut(k) {
            Some(v) => {
                let new_count = self.new_count();
                self.search_count = new_count;
                println!("Found key {} value {}", &k, &v);
            }
            None => println!("cannot find key"),
        }
    }
    
    fn new_count(&self) -> u64 {
        self.search_count + 1
    }
}

fn main() {
    println!("Hello, world!");
    let mut f = Foo{map: HashMap::new(), search_count: 0};
    f.check_and_count("k1");
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src/main.rs:12:33
   |
10 |         match self.map.get_mut(k) {
   |               -------- mutable borrow occurs here
11 |             Some(v) => {
12 |                 let new_count = self.new_count();
   |                                 ^^^^ immutable borrow occurs here
13 |                 self.search_count = new_count;
14 |                 println!("Found key {} value {}", &k, &v);
   |                                                       -- mutable borrow later used 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.

1 Like

In order for new_count to have a mutable borrow, no other mutable borrows may exist.
As long as v is in use, self is still borrowed mutably and cannot be mutated by anything else (because of get_mut).
If you reorder the code to print and then increase the search_count it would compile:

        match self.map.get_mut(k) {
            Some(v) => {
                println!("Found key {} value {}", &k, &v);
                let new_count = self.new_count();
                self.search_count = new_count;
            }
            None => println!("cannot find key"),
        }

Take into consideration that the self.map isn't being mutated, consider calling map.get instead of get_mut, and your code will compile as is.

To clarify, self.new_count() does not need a mutable borrow, but that's a minor thing.

The fact of the matter is that I am using this example to mimic some business logic in my program, which has the following:

it needs to update the self.map after calling self.new_count().

Hence, I am looking for something that allow a get_mut call and do something with it after the self.new_count() call.

I am also trying to understand more about this statement:

As long as v is in use, self is still borrowed mutably and cannot be mutated by anything else (because of get_mut ).

Is there a way to get mutable map only, not the whole self ?

Sorry, I miss-read the code...
The point of mutable is that it has exclusive access to self, while v is in scope (thanks to NLL, it can be in the same scope and the compiler can confirm that everything OK).

It will be possible if you move new_count into check_and_count, otherwise it won't be possible because the borrow checker doesn't check that other functions that take &self won't use self.map, which is exclusively borrowed by v.

By using a Mutex or a RefCell you may solve this problem:

use std::collections::HashMap;
use std::cell::RefCell;

struct Foo {
    map: RefCell<HashMap<String, String>>,
    search_count: u64,
}

impl Foo {
    fn check_and_count(&mut self, k: &str) {
        let mut map = self.map.borrow_mut();
        match map.get_mut(k) {
            Some(v) => {
                let new_count = self.new_count();
                self.search_count = new_count;
                println!("Found key {} value {}", &k, &v);
            }
            None => println!("cannot find key"),
        }
    }
    
    fn new_count(&self) -> u64 {
        self.search_count + 1
    }
}

fn main() {
    println!("Hello, world!");
    let mut f = Foo{map: RefCell::new(HashMap::new()), search_count: 0};
    f.check_and_count("k1");
}
1 Like

You can either inline new_count or you can pass in all the fields needed to calculate new_count explicitly.

fn new_count(search_count: u64) -> u64 {
    search_count + 1
}

This is a limitaiton of the Rust borrow checker, it will never do inter-procedural analysis. So you have to help it out in some places. If new_count requires access to a large portion of Foo, split your type in two parts, grouping all of the data needed for new_count into a seperate type so that it's easier to pass around.

1 Like

Thanks. And I'm wondering why the original error message says *self , not just self. Is there a pointer from v (or the map) back as a *self?

Furthermore, inside the check_and_count method, the incoming self is already a mutable reference that can be used. Is v using that incoming reference, or a different one? I think self.new_count() is trying to use the incoming self reference, right?

I'm not really sure about why the compiler complains about *self and not self, although that how it always is AFAIK.
The mut in check_and_count is only required to mutate the search_count field. If you remove the mut and the assignment to search_count the code should still compile.

Edit:
*self is referring to a reference of the struct, and not the struct itself (like if it was on the stack, and not behind a reference)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.