Choosing Collection Types
Choose the collection whose operations match the program’s access pattern: sequence, queue, unordered lookup, ordered lookup, unique set, or owned text.
What it is
Rust’s standard collections overlap enough that many problems can be solved several ways. The idiomatic choice is the one that makes the main operation cheap and the code’s intent obvious.
Start with the question the data structure answers.
If the answer is “in this order,” think Vec.
If it is “next item to process,” think VecDeque.
If it is “value for this key,” think HashMap.
If it is “keys in sorted order,” think BTreeMap.
If it is “owned UTF-8 text,” think String.
How it works
Vec<T> is the default growable sequence.
It is compact, contiguous, and efficient for iteration and push/pop at the end.
VecDeque<T> trades physical contiguity for efficient operations at both ends.
HashMap<K, V> provides unordered key lookup.
BTreeMap<K, V> and BTreeSet<T> provide ordered traversal and range-style operations.
Use sets when the value itself is the key.
Use maps when each key carries associated data.
Use String for owned text and &str for borrowed text views.
The standard collections overview deliberately recommends starting with Vec or HashMap for many generic storage problems.
Move to a more specialized collection when its behavior is part of correctness or a measured performance need: front removal, sorted ranges, uniqueness, priority, or text ownership.
Example
use std::collections::{BTreeMap, HashMap, VecDeque};
fn main() {
let mut history = Vec::new();
history.push("opened");
history.push("saved");
let mut queue = VecDeque::new();
queue.push_back("parse");
assert_eq!(queue.pop_front(), Some("parse"));
let mut counts = HashMap::new();
*counts.entry("warning").or_insert(0) += 1;
let ordered = BTreeMap::from([("apple", 3), ("banana", 2)]);
assert_eq!(ordered.keys().copied().collect::<Vec<_>>(), ["apple", "banana"]);
}More realistic example
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
fn main() {
let mut recent_errors = VecDeque::with_capacity(100);
recent_errors.push_back("timeout");
recent_errors.push_back("reset");
let mut seen_hosts = HashSet::new();
assert!(seen_hosts.insert("api-1"));
assert!(!seen_hosts.insert("api-1"));
let mut by_status = HashMap::new();
*by_status.entry(500).or_insert(0) += 1;
let timeline = BTreeMap::from([(10_u64, "start"), (20, "connect"), (30, "done")]);
let visible: Vec<_> = timeline.range(10..=20).map(|(_, event)| *event).collect();
assert_eq!(recent_errors.pop_front(), Some("timeout"));
assert_eq!(by_status.get(&500), Some(&1));
assert_eq!(visible, ["start", "connect"]);
}This example uses four collections because the problem has four access patterns: queueing, membership, keyed counting, and ordered range display.
Common errors
thread 'main' panicked at 'index out of bounds'This is a symptom of forcing a sequence model onto uncertain keyed or optional data.
If the real question is “is this key present?” use a map or set; if it is “is this index valid?” use get.
performance regression from repeated front removalVec::remove(0) is O(n) because all later elements shift.
Use VecDeque for sustained queue workloads.
Best practice
- ✅ Default to
Vec<T>for ordered, growable lists unless front operations or keyed lookup dominate. - ✅ Use
VecDeque<T>for queues and sliding windows. - ✅ Use
HashMap<K, V>for fast unordered lookup by key. - ✅ Use
BTreeMap<K, V>orBTreeSet<T>when sorted order or range traversal is required. - ✅ Use
Stringonly for owned text; borrow as&strat API boundaries. - ✅ Use
HashSet<T>orBTreeSet<T>when membership is the data and there is no associated value. - ✅ Use
BinaryHeapfor priority queues where only the next highest-priority item is needed.
Pitfalls
- ⚠️ Do not use
Vec<(K, V)>for frequent key lookup unless the collection is tiny or ordering by insertion is the point. - ⚠️ Do not use
HashMapwhen deterministic sorted output is part of correctness. - ⚠️ Do not use
Vec::remove(0)for queue workloads; use VecDeque. - ⚠️ Do not allocate owned strings for read-only APIs. See Borrowing Strings and Slices.
- ⚠️ Do not choose a collection solely from asymptotic complexity; constants, cache locality, ordering, and clarity matter.
- ⚠️ Do not use
LinkedListby reflex for insertion-heavy code;VecorVecDequeis usually faster and simpler unless a specific linked-list operation is required.
See also
Vec · VecDeque · HashMap · BTreeMap and BTreeSet · Set Collections with HashSet and BTreeSet · BinaryHeap Priority Queues · String and str · Capacity and Reallocation · Borrowing Strings and Slices · Collections & Strings
Sources
- Standard library collections overview — std, https://doc.rust-lang.org/std/collections/index.html
- The Rust Programming Language, ch. 8 “Common Collections” — the-book, https://doc.rust-lang.org/book/ch08-00-common-collections.html
- Standard library
Vec<T>docs — std, https://doc.rust-lang.org/std/vec/struct.Vec.html - Standard library
HashMapdocs — std, https://doc.rust-lang.org/std/collections/struct.HashMap.html - Standard library
BTreeMapdocs — std, https://doc.rust-lang.org/std/collections/struct.BTreeMap.html - Standard library
VecDequedocs — std, https://doc.rust-lang.org/std/collections/struct.VecDeque.html
