Lesson 12
Collections
Work with Vec, String, and HashMap -- Rust's most commonly used collection types.
Vec: Growable Arrays
Vec<T> is Rust's growable array type. Create one with vec![] or Vec::new(), add items with .push(), and access elements by index or with .get(). Vectors own their data and free it when they go out of scope.
HashMap: Key-Value Pairs
HashMap<K, V> stores key-value pairs for fast lookups. Insert with .insert(), look up with .get() (returns Option), and iterate with a for loop. You need to bring it in with use std::collections::HashMap.
Iterators
Rust's iterators are lazy and composable. Chain methods like .iter(), .map(), .filter(), and .collect() to transform collections in clean, readable pipelines. They compile down to the same performance as hand-written loops.
Try it
use std::collections::HashMap;
fn main() {
// --- Vec<T> ---
println!("--- Vectors ---");
let mut fruits = vec!["apple", "banana", "cherry"];
println!(" Fruits: {:?}", fruits);
fruits.push("date");
fruits.push("elderberry");
println!(" After push: {:?}", fruits);
println!(" Length: {}", fruits.len());
println!(" First: {}", fruits[0]);
// Safe access with .get()
match fruits.get(99) {
Some(f) => println!(" Index 99: {}", f),
None => println!(" Index 99: out of bounds (but no crash!)"),
}
// --- Iterators ---
println!("\n--- Iterator Magic ---");
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evens: Vec<&i32> = numbers.iter().filter(|&&n| n % 2 == 0).collect();
println!(" Evens: {:?}", evens);
let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
println!(" Doubled: {:?}", doubled);
let sum: i32 = numbers.iter().sum();
println!(" Sum of 1..10: {}", sum);
// Chaining: sum of squares of even numbers
let sum_of_even_squares: i32 = numbers
.iter()
.filter(|&&n| n % 2 == 0)
.map(|n| n * n)
.sum();
println!(" Sum of even squares: {}", sum_of_even_squares);
// --- HashMap ---
println!("\n--- HashMap ---");
let mut scores: HashMap<&str, i32> = HashMap::new();
scores.insert("Alice", 95);
scores.insert("Bob", 87);
scores.insert("Charlie", 92);
// HashMap does not keep insertion order, so this line can print in any order
println!(" Scores: {:?}", scores);
// Look up a value
if let Some(score) = scores.get("Alice") {
println!(" Alice's score: {}", score);
}
// Update a value
scores.insert("Bob", 91);
println!(" Bob's updated score: {}", scores["Bob"]);
// entry API: insert only if key is missing
scores.entry("Diana").or_insert(88);
scores.entry("Alice").or_insert(0); // won't overwrite
println!(" After entry: {:?}", scores);
// --- Word count: a classic HashMap example ---
println!("\n--- Word Count ---");
let text = "the quick brown fox jumps over the lazy fox";
let mut word_count: HashMap<&str, i32> = HashMap::new();
for word in text.split_whitespace() {
let count = word_count.entry(word).or_insert(0);
*count += 1;
}
let mut counts: Vec<(&&str, &i32)> = word_count.iter().collect();
counts.sort_by_key(|&(word, _)| *word);
for (word, count) in counts {
println!(" {}: {}", word, count);
}
}Challenge
Modify the word count example to count character frequency in a string instead. Use .chars() to iterate over characters.
Previous: Error Handling
Next: Traits