Want to add to HashMap using pattern match, get borrow mutable more than once at a time
You have to use the Entry "pattern":
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
fn main() {
let mut words = vec!["word1".to_string(), "word2".to_string(), "word1".to_string(), "word3".to_string()];
let mut wordCount = HashMap::<String, u32>::new();
for w in words {
let val = match wordCount.entry(w) {
Vacant(entry) => entry.insert(0),
Occupied(entry) => entry.into_mut(),
};
// do stuff with the value
*val += 1;
}
for k in wordCount.iter() {
println!("{:?}", k);
}
}
The Entry object allows you to insert a value if its missing, or to modify it if it already exists.
https://doc.rust-lang.org/stable/std/collections/hash_map/enum.Entry.html
HashMap::entry()
is the method to use here. In most cases you want to use with Entry::or_insert()
to insert a value:
for word in line.split(" ") {
*c.entry(word).or_insert(0) += 1;
}
In case the value to be inserted need to be expensively calculated, you can use Entry::or_insert_with()
to make sure the computation is only executed when it needs to. Both or_insert
methods will probably cover all of your needs. But if you, for whatever reason, want to do something else, you can still simply match
on the Entry
enum.
This is basically not an issue anymore. With non-lexical lifetimes (NLL), your code compiles without problems. Your example on the Playground.
NLL is a new way the compiler reasons about borrows. NLL has been enabled in Rust 2018 (≥ 1.31). It is planned to be enabled in Rust 2015 eventually as well. You can read more about NLL and editions in this official blog post.
In this particular case, I still think A.B.'s answer (entry(word).or_insert(0)
) is the best solution, simply because it is very concise.