3

I'm writing a function that works with a HashMap and at various points in the function I want to check if any of the values in the map meet a condition and if any does return the key for just one of the entries that meets the condition.

So far, I tried to first iterate over references to check my condition, and then use remove_entry to get an owned copy of the key rather than a borrow:

use std::{collections::HashMap, hash::Hash};

fn remove_first_odd<K: Hash + Eq>(map: &mut HashMap<K, u64>) -> Option<K> {
    let maybe_entry = map.iter().find(|(_, &value)| value & 1 == 1);

    match maybe_entry {
        Some((key_ref, _value_ref)) => {
            let (key_owned, _value_owned) = map.remove_entry(key_ref).unwrap();
            Some(key_owned)
        }
        None => None,
    }
}

but this fails borrow checking, because I can't mutate the map while I am still holding a reference to one of the keys in it:

error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable
 --> src/lib.rs:8:45
  |
4 |     let maybe_entry = map.iter().find(|(_, &value)| value & 1 == 1);
  |                       --- immutable borrow occurs here
...
8 |             let (key_owned, _value_owned) = map.remove_entry(key_ref).unwrap();
  |                                             ^^^^------------^^^^^^^^^
  |                                             |   |
  |                                             |   immutable borrow later used by call
  |                                             mutable borrow occurs here

I believe that this is possible, because as long as remove_entry only dereferences my key before mutation and not after, this should be sound.

So is there a safe API to do this? If not, can one be created?

Note that:

  • I can't clone my keys because they are generic without a clone bound
  • I can't consume the HashMap with something like .into_iter()
  • I only need to remove a single entry, not all of the entries that match
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
timotree
  • 1,325
  • 9
  • 29
  • 3
    "I can't clone my keys" in which world ? I don't think you can in safe mode. – Stargateur Mar 28 '19 at 05:01
  • 1
    I'm afraid it's not possible, at least not if you want to return the key which has been removed. If you do not need to return the value you could use `retain` to remove the pair that fits your criteria. – hellow Mar 28 '19 at 07:15
  • @Stargateur I edited it to make it more clear – timotree Mar 28 '19 at 12:10
  • @timotree so let me be more clear, key in a hashmap should be clonable in 99% of use case. That very odd situation you have here. I advice you to describe why you can't clone the key. – Stargateur Mar 28 '19 at 12:13
  • 1
    See also [How to iterate through a Hashmap, print the key/value and remove the value in Rust?](https://stackoverflow.com/q/45724517/155423); [Removing items from a BTreeMap or BTreeSet found through iteration](https://stackoverflow.com/q/32913368/155423); [Rust loop on HashMap while borrowing self](https://stackoverflow.com/q/45311896/155423); – Shepmaster Mar 31 '19 at 18:34

0 Answers0