Hash Maps and Hash Sets

HashSet Operations

HashSet<T, S> provides $O(1)$ average-case insertion, removal, membership testing, and subset/superset/disjoint queries for unique elements implementing Eq + Hash.

Single-Element and Predicate Operations on HashSet<T>

Method Signature Return Type Behavior
set.insert(val) fn insert(&mut self, value: T) -> bool bool Inserts val if absent (true); if an equal element already exists, keeps the old element and returns false
set.replace(val) fn replace(&mut self, value: T) -> Option<T> Option<T> Inserts val, replacing and returning the existing equal element Some(old) if present
set.contains(&q) fn contains<Q>(&self, value: &Q) -> bool bool Returns true if the set contains an element equal to q
set.get(&q) fn get<Q>(&self, value: &Q) -> Option<&T> Option<&T> Returns a reference to the actual stored element &T matching q
set.remove(&q) fn remove<Q>(&mut self, value: &Q) -> bool bool Removes the matching element and returns true if it was present
set.take(&q) fn take<Q>(&mut self, value: &Q) -> Option<T> Option<T> Removes and returns ownership of the matching element Some(T)
set.retain(f) fn retain<F: FnMut(&T) -> bool>(&mut self, f: F) () Keeps only elements where f(&elem) returns true

Comparing insert() vs. replace(), and remove() vs. take()

When T is a custom struct whose Eq and Hash implementations compare only an ID field (or case-insensitive text), .insert(new_val) leaves the old value untouched if an equal item is already in the set, whereas .replace(new_val) swaps in the new item and returns the old one:

use std::collections::HashSet;

fn main() {
    let mut tags = HashSet::from([String::from("rust"), String::from("systems")]);

    // 1. `insert` returns `true` for a new element, `false` for a duplicate:
    println!("Insert 'async': {}", tags.insert(String::from("async")));
    println!("Insert 'rust':  {}", tags.insert(String::from("rust")));

    // 2. Querying with `&str` via `get` and `take` (recovering the owned `String`):
    println!("Get 'systems':  {:?}", tags.get("systems"));
    if let Some(owned_tag) = tags.take("systems") {
        println!("Taken owned String: '{owned_tag}'");
    }
    println!("Contains 'systems' after take: {}", tags.contains("systems"));
}
Insert 'async': true
Insert 'rust':  false
Get 'systems':  Some("systems")
Taken owned String: 'systems'
Contains 'systems' after take: false

Relational Set Predicates (is_disjoint, is_subset, is_superset)

Method Mathematical Relation Meaning
a.is_disjoint(&b) $A \cap B = \emptyset$ true if a and b share no elements in common
a.is_subset(&b) $A \subseteq B$ true if every element of a is also in b (short-circuits to false in $O(1)$ if a.len() > b.len())
a.is_superset(&b) $A \supseteq B$ Equivalent to b.is_subset(&a)
use std::collections::HashSet;

fn main() {
    let read_write = HashSet::from(["read", "write"]);
    let all_perms = HashSet::from(["read", "write", "delete", "admin"]);
    let public_only = HashSet::from(["public"]);

    println!("read_write ⊆ all_perms:     {}", read_write.is_subset(&all_perms));
    println!("all_perms ⊇ read_write:     {}", all_perms.is_superset(&read_write));
    println!("read_write ∩ public = ∅:    {}", read_write.is_disjoint(&public_only));
}
read_write ⊆ all_perms:     true
all_perms ⊇ read_write:     true
read_write ∩ public = ∅:    true