Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Popping" a value from a HashSet

I can't seem to find a way to pop a (random) value from a HashSet. Inspired by other code samples, I wrote the following:

my_set.iter().next().map(|i| my_set.take(i).unwrap())

I.e get an iterator on the set's values, take the first value (a reference) from it, and then run my_set.take() using the reference previously obtained, to get the value itself (not the reference) and remove it from the set.

This doesn't compile due to:

error[E0500]: closure requires unique access to `my_set` but it is already borrowed
   |
32 |         my_set.iter().next().map(|i| my_set.take(i).unwrap())
   |         ------               --- ^^^ ------ second borrow occurs due to use of `my_set` in closure
   |         |                    |   |
   |         |                    |   closure construction occurs here
   |         |                    first borrow later used by call
   |         borrow occurs here

I've tried many, many variations on this, but they all fail due to an immutable borrow then being borrowed as mutable (error 502).

Can any one recommend a way to rewrite the above?

like image 486
Maan Avatar asked Oct 14 '22 20:10

Maan


2 Answers

If you're okay with cloning the item removed you can do:

let elem = set.iter().next().unwrap().clone();
set.remove(&elem);

Playground

Or with a guard against the set being empty:

if let Some(elem) = set.iter().next().cloned() {
    set.remove(&elem);
}

Playground

like image 167
John Kugelman Avatar answered Oct 20 '22 17:10

John Kugelman


This feels hacky, but you can use HashSet::retain with an impure function:

let mut flag = false;
my_set.retain(|_| mem::replace(&mut flag, true));

(playground)

The element removed is truly arbitrary — I ran the code on the playground a few times, and obtained different results.


As interjay mentioned in a comment, this approach iterates over the entire set just to remove one value, so the cost is generally more significant than cloning one value. Therefore, only use this workaround when the element really cannot be cloned.

like image 29
L. F. Avatar answered Oct 20 '22 16:10

L. F.