Can I do this in one expression?
*map.entry(key).or_default() += 1;
let count = *map.get(&key).unwrap();
Ideally, I would like this to be an atomic operation, but I can lock if needed.
/?version=stable&mode=debug&edition=2021&gist=df6778cd1312c31eb230a545c6c2b6ce
Can I do this in one expression?
*map.entry(key).or_default() += 1;
let count = *map.get(&key).unwrap();
Ideally, I would like this to be an atomic operation, but I can lock if needed.
https://play.rust-lang./?version=stable&mode=debug&edition=2021&gist=df6778cd1312c31eb230a545c6c2b6ce
Share Improve this question edited Jan 17 at 15:49 fadedbee asked Jan 17 at 15:27 fadedbeefadedbee 44.8k48 gold badges199 silver badges351 bronze badges 5- 3 Post a minimal reproducible example. It is not clear what types we are talking about here. – Eugene Sh. Commented Jan 17 at 15:33
- @EugeneSh. I've added a Rust Playground link. – fadedbee Commented Jan 17 at 15:52
- 1 Can you clarify what you mean by "an atomic operation"? Are you just focused on a single expression? Or just avoiding the second key lookup? Or is your map really synchronized with other threads and needs the change to appear atomic? I ask because you mention "atomic"s and "lock"s but you've accepted an answer that doesn't address those (at least not in the normal - i.e. threading - sense) – kmdreko Commented Jan 17 at 18:58
- I believe that by “atomic” OP means a single line and a single lookup, not in the sense of concurrency/thread-safety. – BallpointBen Commented Jan 18 at 15:56
- @BallpointBen I meant atomic as in being called from multiple threads, yielding a convergent result regardless of ordering. I hadn't thought it through though, as that would require a lock-free Map - i.e. the map-lookup/insert and counter-increment would both need to be atomic in combination, which is probably beyond what can be achieved with atomics. – fadedbee Commented Jan 20 at 7:49
2 Answers
Reset to default 2You can:
let count = *map.entry(key).and_modify(|v| *v += 1).or_insert(1);
This will:
- Get or create the entry
- If it exists, increment it
- If it doesn't exist, insert 1
- Return a reference to the value, which we then dereference
I don't know if you consider the following initialisation of count
as only one expression, but at least it avoids a second lookup with .get()
.
I suppose the atomic operation is on the integer, not the whole map (maybe I'm wrong).
fn main() {
let mut map = std::collections::HashMap::new();
map.insert(String::from("a"), std::sync::atomic::AtomicUsize::new(100));
//
for k in ["a", "b"] {
let key = String::from(k);
let count = map
.entry(key)
.or_default()
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
+ 1;
println!("{} ~~> {}", k, count)
}
//
println!("{:?}", map);
}
/*
a ~~> 101
b ~~> 1
{"a": 101, "b": 1}
*/