Phase 3Data structures

#12 Collections

Vec, HashMap, iterators

Vectors (Vec<T>)

A Vec<T> is a growable array. It stores elements of the same type on the heap and is the most commonly used collection in Rust.

Creating and using vectors
// Create an empty vector and push elements
let mut fruits: Vec<&str> = Vec::new();
fruits.push("apple");
fruits.push("banana");
fruits.push("cherry");
println!("Fruits: {:?}", fruits);

// Create with the vec! macro
let numbers = vec![1, 2, 3, 4, 5];

// Access elements
println!("First: {}", numbers[0]);       // panics if out of bounds
println!("Safe: {:?}", numbers.get(10));  // returns Option<&T>

Common vector methods include push(), pop(), len(), is_empty(), contains(), and remove().

Vector operations
let mut nums = vec![10, 20, 30, 40];

nums.push(50);            // Add to end
let last = nums.pop();    // Remove from end -> Some(50)
nums.remove(1);           // Remove at index 1 -> 20

println!("Length: {}", nums.len());       // 3
println!("Has 10: {}", nums.contains(&10)); // true

HashMaps

A HashMap<K, V> stores key-value pairs. Keys must implement Eq and Hash. You need to import it from std::collections.

Creating and using HashMaps
use std::collections::HashMap;

let mut scores: HashMap<&str, i32> = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
scores.insert("Charlie", 92);

// Access values
if let Some(score) = scores.get("Alice") {
    println!("Alice scored: {}", score);
}

// Iterate over key-value pairs
for (name, score) in &scores {
    println!("{}: {}", name, score);
}

The entry() API lets you insert a value only if the key does not already exist:

Entry API
let mut word_count: HashMap<&str, i32> = HashMap::new();

let words = vec!["hello", "world", "hello", "rust"];
for word in words {
    let count = word_count.entry(word).or_insert(0);
    *count += 1;
}
println!("{:?}", word_count);
// {"hello": 2, "world": 1, "rust": 1}

Iterators

Iterators are one of Rust's most powerful features. Any type that implements the Iterator trait can be used in a for loop and with iterator adapter methods.

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

// iter() borrows each element
for n in numbers.iter() {
    println!("{}", n);
}

// into_iter() takes ownership
for n in numbers.into_iter() {
    println!("{}", n);
}

// iter_mut() borrows each element mutably
let mut values = vec![1, 2, 3];
for n in values.iter_mut() {
    *n *= 2;
}
println!("{:?}", values); // [2, 4, 6]

Common iterator methods

Iterator adapters transform iterators. They are lazy: nothing happens until you consume the iterator with a method like collect() or sum().

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

// map: transform each element
let doubled: Vec<i32> = numbers.iter()
    .map(|&n| n * 2)
    .collect();
println!("Doubled: {:?}", doubled); // [2, 4, 6, 8, 10]

// filter: keep elements matching a predicate
let evens: Vec<i32> = (1..=10)
    .filter(|n| n % 2 == 0)
    .collect();
println!("Evens: {:?}", evens); // [2, 4, 6, 8, 10]

// sum: consume the iterator into a total
let total: i32 = numbers.iter().sum();
println!("Sum: {}", total); // 15

You can chain multiple adapters together to build powerful data pipelines:

Chaining iterators
let words = vec!["hello", "world", "rust", "is", "great"];

let result: Vec<String> = words.iter()
    .filter(|w| w.len() > 3)
    .map(|w| w.to_uppercase())
    .collect();

println!("{:?}", result); // ["HELLO", "WORLD", "RUST", "GREAT"]

Summary

Quick reference
// Vectors
let mut v = Vec::new();     // or vec![1, 2, 3]
v.push(value);              // add element
v.pop();                    // remove last
v.get(index);               // safe access -> Option

// HashMaps
use std::collections::HashMap;
let mut m = HashMap::new();
m.insert(key, value);       // add entry
m.get(&key);                // lookup -> Option
m.entry(key).or_insert(v);  // insert if absent

// Iterators
.iter()      // borrow elements
.map(|x| y)  // transform
.filter(|x| bool)  // keep matching
.collect()   // gather into collection
.sum()       // total numeric values

Your turn

Try running the collections demo with cargo run and verify it compiles cleanly with cargo check:

terminal — cargo
user@stemlegacy:~/collections-demo$