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.
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.
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");
}
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.
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)