If you've come from Python or JavaScript, you might think "just use a list" and call it a day. But Rust has multiple collection types, and choosing the right one matters.

The big three are: Vec (growable arrays), HashMap (key-value stores), and &[T] (slices, aka "views" into existing data).

Vec — When You Need a List

A Vec is a growable array. Unlike arrays in many languages, it can resize:

let mut numbers = Vec::new();
numbers.push(1);
numbers.push(2);
numbers.push(3);
// numbers is now [1, 2, 3]

You can also use the vec! macro for a shorthand:

let numbers = vec![1, 2, 3, 4, 5];

Common operations:

// Get by index (returns Option)
let first = numbers.get(0);           // Some(&1)
let hundred = numbers.get(100);        // None

// Iterate
for n in &numbers {
    println!("{}", n);
}

// Modify
numbers.push(6);
numbers.remove(0);                     // Remove first element

// Check length
println!("{}", numbers.len());         // 5

When to Use Vec

HashMap — When You Need a Dictionary

A HashMap stores key-value pairs. Fast lookup by key:

use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert("Alice", 100);
scores.insert("Bob", 85);
scores.insert("Charlie", 92);

// Lookup
if let Some(score) = scores.get("Alice") {
    println!("Alice's score: {}", score);
}

// Update
scores.insert("Alice", 110);  // Overwrites

// Or use entry API for conditional updates
scores.entry("Bob").and_modify(|s| *s += 10);

When to Use HashMap

The Slice — When You Don't Own

A slice (&[T]) is a view into existing data. It doesn't own anything:

let array = [1, 2, 3, 4, 5];
let slice = &array[1..4];  // [2, 3, 4]

fn process(slice: &[i32]) {
    for n in slice {
        println!("{}", n);
    }
}

Functions accept slices when they don't need to own the data. This is why so many standard library functions take &[T] — they work on arrays, vectors, or any contiguous sequence.

Choosing Between Vec and HashMap

| Operation | Vec | HashMap | |-----------|-----|---------| | Insert at end | O(1) amortized | N/A | | Lookup by index | O(1) | N/A | | Lookup by key | O(n) | O(1) | | Iterate | O(n) | O(n) | | Sorted? | No (by default) | No |

Rule of thumb: If you're doing more lookups by key than insertions, use HashMap. If you're accessing by position, use Vec.

Other Collections Worth Knowing

You won't need these immediately, but they exist when you need them.

Your Turn

Write a program that:

  1. Takes a list of fruit names
  2. Counts how many times each fruit appears
  3. Prints the results sorted alphabetically

Use a HashMap to count, then convert to a Vec, sort it, and print.

let fruits = vec!["apple", "banana", "apple", "orange", "banana", "apple"];
// Expected output:
//   apple: 3
//   banana: 2
//   orange: 1

Next up: Chapter 8 — Lifetimes. The part that scares everyone, demystified.