Idiomatic way to remove entries from hashmap by condition and move their values out

⚓ Rust    📅 2026-03-26    👤 surdeus    👁️ 3      

surdeus

Hello.
There is a hash map HashMap::<TaskId, sync::oneshot::Sender<TaskResult>>.
From time to time, some process:

  1. gets all keys from this map
  2. checks their status in external system
  3. if status is known, it sends it via the Sender and removes the entry not to check it again.

But one shot sender moves itself into the method: oneshot::Sender::send(self, value) (it prevents oneshot from being called again)

So, I need to remove entry from the map (the only way to get value itself, as reference isn't enough).

I've come to several approaches, but all of them aren't elegant: I clone all keys, access map two times and so on.

    // For all completed tasks report their status and remove them not to check them again
    // Approach 1:
    for task_id in map.keys().cloned().collect::<Vec<_>>() { // <-- redundant copy
        if let Some(task_result) = get_task_result(task_id) {
            map.remove(&task_id)
                .expect("Can't be") // <-- silly
                .send(task_result)
                .unwrap();
        }
    }

    // Approach 2:
    let completed_tasks: Vec<_> = map
        .keys()
        .filter_map(|&task_id| get_task_result(task_id).map(|result| (task_id, result)))
        .collect(); // <-- redundant copy
    for (task_id, task_result) in completed_tasks {
        map.remove(&task_id)
            .expect("can't be") // <-- Silly
            .send(task_result)
            .unwrap();
    }

There is HashMap::retain of course, but it can't tell in advance if I will remove an entry or not, so I get reference.

There is HashMap:extract_if, but it returns only key (taskId in my case) and value (sender) but doesn't allow me to map sender, so I can't send anything.

I wish there were something like mutable ref iterator over entities, i.e:

for entity in map.entities() {
  let k:&Ket= entity.key();
  // but at the same time
  let v:Value = entitiy.remove_value_and_return_it() 
}

I can solve it with RefCell probably, or with unsafe. But how would you solve it?

Thank you in advance.

PS: One might argue that iterating over keys is a bad idea, so they should better be stored in a vector. But I would need to synchronize both collections and still get value from the map.

2 posts - 2 participants

Read full topic

🏷️ Rust_feed