Hash Maps and Hash Sets
Set Union, Intersection, and Difference
HashSet<T> supports the four fundamental mathematical set operations—union, intersection, difference, and symmetric difference—both as lazy non-allocating iterators (yielding &T) and as eager bitwise operators on borrowed sets (&a | &b, &a & &b, &a - &b, &a ^ &b) that return a new owned HashSet<T> when T: Clone.
Set Algebra Reference Table
Set A = {1, 2, 3} Set B = {3, 4, 5}
Union (A ∪ B): {1, 2, 3, 4, 5} (in A, in B, or in both)
Intersection (A ∩ B): {3} (in BOTH A and B)
Difference (A \ B): {1, 2} (in A, but NOT in B)
Symmetric Difference (A △ B): {1, 2, 4, 5} (in A or B, but NOT both)
| Operation | Lazy Iterator Method (Item = &T) |
Eager Operator (-> HashSet<T>, requires T: Clone) |
|---|---|---|
| Union ($A \cup B$) | a.union(&b) |
&a | &b |
| Intersection ($A \cap B$) | a.intersection(&b) |
&a & &b |
| Difference ($A \setminus B$) | a.difference(&b) |
&a - &b |
| Symmetric Difference ($A \triangle B$) | a.symmetric_difference(&b) |
&a ^ &b |
Lazy Iterators vs. Eager Operators
Prefer the lazy iterator methods (.union(), .intersection(), .difference(), .symmetric_difference()) when you only need to iterate over the result, count matches, or collect into a Vec—they perform zero heap allocations:
use std::collections::HashSet;
fn main() {
let a = HashSet::from([1, 2, 3]);
let b = HashSet::from([3, 4, 5]);
// 1. Lazy iterator methods (no intermediate HashSet allocated):
let common_count = a.intersection(&b).count();
println!("Common element count: {common_count}");
// 2. Eager operator syntax (`&a | &b`, `&a & &b`, `&a - &b`, `&a ^ &b`):
let union_set: HashSet<i32> = &a | &b;
let inter_set: HashSet<i32> = &a & &b;
let diff_set: HashSet<i32> = &a - &b;
let sym_diff_set: HashSet<i32> = &a ^ &b;
let mut u: Vec<i32> = union_set.into_iter().collect();
let mut d: Vec<i32> = diff_set.into_iter().collect();
let mut s: Vec<i32> = sym_diff_set.into_iter().collect();
u.sort_unstable();
d.sort_unstable();
s.sort_unstable();
println!("Union (A | B): {u:?}");
println!("Intersection (A & B): {inter_set:?}");
println!("Difference (A - B): {d:?}");
println!("Symmetric Difference (A ^ B): {s:?}");
}
Common element count: 1
Union (A | B): [1, 2, 3, 4, 5]
Intersection (A & B): {3}
Difference (A - B): [1, 2]
Symmetric Difference (A ^ B): [1, 2, 4, 5]
In-Place Set Updates
When modifying an existing mut set_a: HashSet<T> in place without allocating a brand-new set:
- In-place union ($A \leftarrow A \cup B$):
set_a.extend(set_b) - In-place intersection ($A \leftarrow A \cap B$):
set_a.retain(|x| set_b.contains(x)) - In-place difference ($A \leftarrow A \setminus B$):
set_a.retain(|x| !set_b.contains(x))