Правила наложения имен, наложенные компилятором, требуют перемещения значений вперед и назад. Значения могут быть истощены из набора, хотя и безусловно. Однако мы можем отправить определенные значения обратно, если будем отслеживать, какие из них следует переместить, а какие следует оставить в новом наборе. После этого retain
позволяет нам удалить общие значения из второго набора.
use std::collections::HashSet;
use std::hash::Hash;
/// Extracts the common values in `a` and `b` into a new set.
fn inplace_intersection<T>(a: &mut HashSet<T>, b: &mut HashSet<T>) -> HashSet<T>
where
T: Hash,
T: Eq,
{
let x: HashSet<(T, bool)> = a
.drain()
.map(|v| {
let intersects = b.contains(&v);
(v, intersects)
})
.collect();
let mut c = HashSet::new();
for (v, is_inter) in x {
if is_inter {
c.insert(v);
} else {
a.insert(v);
}
}
b.retain(|v| !c.contains(&v));
c
}
Использование:
use itertools::Itertools; // for .sorted()
let mut a: HashSet<_> = [1, 2, 3].iter().cloned().collect();
let mut b: HashSet<_> = [4, 2, 3].iter().cloned().collect();
let c = inplace_intersection(&mut a, &mut b);
let a: Vec<_> = a.into_iter().sorted().collect();
let b: Vec<_> = b.into_iter().sorted().collect();
let c: Vec<_> = c.into_iter().sorted().collect();
assert_eq!(&a, &[1]);
assert_eq!(&b, &[4]);
assert_eq!(&c, &[2, 3]);
Детская площадка