Introduction#
Most of the time in Rust, Vec<T> and HashMap<K, V> are all you need. But when you hit message queues, specialized queries, or task schedulers, the default containers start falling short.
The standard std::collections module covers most application needs without third-party crates. Still, it is worth looking beyond just O(1) or O(log N) complexity ratings and paying attention to how data actually sits in memory. Modern CPUs love contiguous memory: good data placement and L1/L2 cache hits often outweigh theoretical algorithmic bounds in practice.
Let’s walk through four key structures from the standard library: VecDeque, BTreeMap, BTreeSet, and BinaryHeap.
1. Collections: VecDeque vs Vec vs LinkedList#
Dynamic Array (Vec<T>)#
Vec<T> stores elements as a contiguous array on the heap. Because of this, iterating over a vector runs very fast, and indexing is rated at O(1).
The vector’s weak spot is inserting or removing elements at the front or middle: you have to shift the entire tail for O(N).
Double-Ended Queue (VecDeque<T>)#
If you need a queue (FIFO) or a ring buffer of recent events, reach for VecDeque<T>.
Under the hood, it is a dynamic ring buffer:
- Push and pop at the back (
push_back/pop_back) —O(1). - Push and pop at the front (
push_front/pop_front) —O(1)without shifting the entire array.
In almost every scenario where you might think about a linked list, VecDeque wins in both speed and memory efficiency.
Linked List (LinkedList<T>)#
Doubly linked LinkedList<T> is almost never seen in real-world Rust code. Each node lives in its own heap allocation. Traversing the list forces the CPU to jump across memory and constantly hit cache misses.
2. Maps: BTreeMap vs HashMap#
For working with key-value pairs, the standard library offers two tools.
Hash Tables (HashMap<K, V> and HashSet<T>)#
- Under the hood: SwissTable algorithm.
- Complexity: lookup, insertion, and deletion run in average
O(1). - Characteristics: element order is arbitrary. Keys must implement
Hash + Eq.
B-Trees (BTreeMap<K, V> and BTreeSet<T>)#
- Under the hood: a B-Tree where each node holds a small array of elements. This packs keys tightly into CPU cache lines.
- Complexity: lookup, insertion, and deletion guarantee
O(log N)time. - Characteristics: keys are always sorted. Keys must implement
Ord.
use std::collections::BTreeMap;
let mut map = BTreeMap::new();
map.insert(3, "Third");
map.insert(1, "First");
// Iterates in ascending key order: 1, then 3
for (key, val) in &map {
println!("{key}: {val}");
}Why choose BTreeMap if HashMap offers O(1)?
- You need keys sorted by default.
- You need range queries via
.range(min..max). - You need predictable runtime guarantees without worst-case
O(N)hash collisions.
3. Priority Queue: BinaryHeap#
BinaryHeap<T> is a binary Max-Heap implemented on top of a vector. The collection is built for task schedulers and event queues where you need to process the highest-priority item first.
- Adding an item via
.push(item)takesO(log N). - Extracting the maximum item via
.pop()takesO(log N). - Peeking at the top item via
.peek()takesO(1).
To turn BinaryHeap into a Min-Heap (popping the smallest item first), wrap values in std::cmp::Reverse(x).
4. Complexity Cheatsheet#
| Collection | Access | Front Insert | Back Insert | Element Ordering |
|---|---|---|---|---|
Vec<T> | O(1) by index | O(N) | O(1) amortized | Unsorted |
VecDeque<T> | O(1) by index | O(1) amortized | O(1) amortized | Unsorted |
HashMap<K, V> | O(1) avg | — | — | Random |
BTreeMap<K, V> | O(log N) | — | — | Sorted |
BinaryHeap<T> | O(1) for Max | — | O(log N) | Priority |
Below are interactive slides for each collection:
Quick Quiz#
A short test to check your understanding of Rust collections:


