18.8Choosing a collection

Last updated June 13, 2026

Vec, String, and HashMap cover most needs, but the standard library has a few more collections worth knowing for the cases where the core three aren't the best fit. This lesson is a decision guide: brief cameos of VecDeque, HashSet, and BTreeMap, and a table to help you pick. You won't reach for these daily, but recognizing when one beats a Vec or HashMap is the mark of knowing the toolbox.

VecDeque: efficient at both ends

A Vec is fast to push and pop at the end, but removing from the front is slow, because every remaining element has to shift down one position. When you need a queue, adding at one end and removing from the other, reach for VecDeque (a double-ended queue):

use std::collections::VecDeque;

fn main() {
    let mut queue = VecDeque::new();
    queue.push_back(1);
    queue.push_back(2);
    queue.push_front(0);

    println!("{:?}", queue.pop_front());   // Some(0)
    println!("{queue:?}");                 // [1, 2]
}
Some(0)
[1, 2]

VecDeque adds and removes efficiently at both ends with push_front/push_back and pop_front/pop_back. Use it for queues (first-in-first-out) and any workload that touches both ends. For a plain stack or a list you only append to, a Vec is simpler and just as fast.

HashSet: membership without values

When you want a collection of unique items and only care whether something is present, not an associated value, use HashSet. It's like a HashMap with keys but no values:

use std::collections::HashSet;

fn main() {
    let mut seen = HashSet::new();
    seen.insert("apple");
    seen.insert("banana");
    seen.insert("apple");          // duplicate: ignored

    println!("{}", seen.len());            // 2
    println!("{}", seen.contains("apple")); // true
}
2
true

HashSet automatically discards duplicates (inserting "apple" twice leaves one) and answers contains quickly. Reach for it for "have I seen this before?", deduplication, or any set-membership question. It also offers set operations like union and intersection. If you find yourself using a HashMap where the values are meaningless placeholders, you wanted a HashSet.

BTreeMap: a map that stays sorted

A HashMap is fast but stores entries in no particular order (you saw its unpredictable iteration order in lesson 18.6). When you need the keys kept sorted, so you can iterate in order or find ranges, use BTreeMap (and its set cousin BTreeSet):

use std::collections::BTreeMap;

fn main() {
    let mut scores = BTreeMap::new();
    scores.insert("Charlie", 3);
    scores.insert("Alice", 1);
    scores.insert("Bob", 2);

    for (name, score) in &scores {
        println!("{name}: {score}");       // always alphabetical by key
    }
}
Alice: 1
Bob: 2
Charlie: 3

BTreeMap iterates in sorted key order, always, regardless of insertion order, because it keeps its keys ordered internally (which requires the key type to be Ord, lesson 16.5). The tradeoff is that lookups are slightly slower than a HashMap's. Choose BTreeMap when ordered iteration or range queries matter; choose HashMap when you only need fast lookup and order is irrelevant (which is more common).

The decision table

A quick guide to the whole family:

You want...Reach for
A sequence, append/read at the endVec<T>
Growable UTF-8 textString
Look up values by key, order doesn't matterHashMap<K, V>
Add/remove at both ends (a queue)VecDeque<T>
Unique items, membership testingHashSet<T>
Key-value pairs kept in sorted orderBTreeMap<K, V>
Unique items kept in sorted orderBTreeSet<T>
Fixed number of elements, known at compile timearray [T; N]

Best practice

Default to Vec and HashMap; they're the right answer most of the time. Switch to a specialized collection only when its specific strength matches your need: VecDeque for front-and-back access, HashSet for uniqueness, BTreeMap/BTreeSet for sorted order. The question to ask is "what operation do I do most, and which collection makes that operation cheap and natural?" Picking the collection that fits the access pattern is most of good data-structure choice.

Quiz time

Question #1

When would you use a VecDeque instead of a Vec?

Show solution

When you need to add and remove efficiently at both ends, typically a queue (add at the back, remove from the front). A Vec is fast at the end but slow to remove from the front (everything shifts down). VecDeque handles both ends efficiently with push_front/pop_front and push_back/pop_back.

Question #2

You want to track which usernames you've already processed, with no associated data, just "seen or not." Which collection, and why?

Show solution

A HashSet<String> (or HashSet<&str>). You only care about membership and uniqueness, not an associated value, which is exactly what a set provides: it discards duplicates and answers contains quickly. Using a HashMap with placeholder values would be a sign you actually wanted a HashSet.

Question #3

What does BTreeMap give you that HashMap doesn't, and what's the tradeoff?

Show solution

BTreeMap keeps its keys in sorted order, so iterating yields entries in order and you can do range queries; HashMap has no defined order. The tradeoff is that BTreeMap lookups are somewhat slower than HashMap's, and its key type must be Ord. Choose BTreeMap when ordered iteration matters, HashMap when you only need fast lookup.

That's the standard collection toolbox. The summary and a capstone quiz close the chapter, and then chapter 19 takes the iteration you've done with for loops throughout this chapter and reveals the powerful machinery beneath it: closures and iterators, where map, filter, and collect turn loops into expressive pipelines.