I feel rather dumb not being able to solve this by myself.
I have a HashMap
where I'd like to modify and maybe remove several entries in order to process them elsewhere.
This is one of several horrible solutions I came up with:
use std::collections::{HashMap, hash_map::Entry};
#[derive(Debug)]
struct SomeObject(u32);
fn main() {
let mut my_map = HashMap::<String, SomeObject>::default();
my_map.insert("xyz".to_string(), SomeObject(123));
my_map.insert("abc".to_string(), SomeObject(321));
// clone(!!) all keys
let keys = my_map.keys().cloned().collect::<Vec<String>>();
// removed objects go here
let mut drained_entries = Vec::new();
for key in keys {
// unnecessary lookup
match my_map.entry(key) {
Entry::Occupied(mut entry) => {
let some_object = entry.get_mut();
some_object.0 -= 1;
if some_object.0 < 200 {
drained_entries.push(entry.remove());
}
}
Entry::Vacant(_entry) => {
//
unreachable!()
}
}
}
println!("retained {:?}", my_map);
println!("drained {:?}", drained_entries);
// prints:
// retained {"abc": SomeObject(320)}
// drained [SomeObject(122)]
}
(playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d56a8d242df3c3979f6d5bdb1250a04d )
The following ideas didn't work out:
-
drain()
doesn't allow filtering and will remove every entry -
iter_mut()
doesn't allow to remove entries -
retain()
doesn't allow to move out the value without cloning -
entry()
doesn't provide an iterator
If there was something like filter_drain()
or occupied_entries()
it would be easy to implement without cloning, double lookups, or excessive memory consumption.
Thank you for helping me out!