I'm trying to learn Rust and I don't understand the following warning message:
Could you explain it to me?
use std::collections::HashMap;
fn main() {
let mut m: HashMap<String, i32> = HashMap::new();
for word in vec!["one", "one", "two"] {
match m.get(word) {
Some(c) => {
m.insert(String::from(word), c + 1);
}
None => {
m.insert(String::from(word), 1);
}
}
}
println!("{:?}", m);
}
warning: cannot borrow `m` as mutable because it is also borrowed as immutable
--> src\main.rs:10:17
|
8 | match m.get(word) {
| - immutable borrow occurs here
9 | Some(c) => {
10 | m.insert(String::from(word), c + 1);
| ^ mutable borrow occurs here - immutable borrow later used here
|
= note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
= warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
= note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.82s
HashMap::insert(&mut m, String::from(word), c + 1)
which however doesn't compile because &mut m is evaluated before c + 1, and that mutable borrow of m invalidates the previous shared borrow that c is holding. However, the borrow checker used to allow this code so for backwards compatibility this can't be an hard error for now, but it may become in the future. You should avoid this pattern if you don't want your code to suddently break with a new version of the compiler.
The simpliest fix is to calculate c + 1 before the insert save it in a temporary variable and then use that in the insert.
Fixed code
use std::collections::HashMap;
fn main() {
let mut m: HashMap<String, i32> = HashMap::new();
for word in vec!["one", "one", "two"] {
match m.get(word) {
Some(c) => {
let new_c = c + 1;
m.insert(String::from(word), new_c);
}
None => {
m.insert(String::from(word), 1);
}
}
}
println!("{:?}", m);
}