Once you can model a single log with structs and enums, you need containers to manage many values at once. The two collections you’ll reach for most in Rust’s standard library are Vec<T> and [[hashmap|HashMap<K, V>]]. We’ll store study logs in vectors and hash maps, then use iteration to summarize them.
Vec Fundamentals
Vec<T> is a growable, heap-backed array. Use push, pop, and iter as needed.
fn main() {
let mut topics = Vec::new();
topics.push("ownership".to_string());
topics.push("borrowing".to_string());
topics.push("enum".to_string());
for topic in &topics {
println!("Upcoming: {}", topic);
}
}
for topic in &topics iterates over immutable references to each element. As long as you don’t take mutable references at the same time, this stays safe.
Vectors with Structs
Managing multiple study logs in a vector makes reporting straightforward.
struct StudyLog {
topic: String,
finished: bool,
}
fn main() {
let logs = vec![
StudyLog { topic: "borrowing".into(), finished: true },
StudyLog { topic: "structs".into(), finished: true },
StudyLog { topic: "enum".into(), finished: false },
];
let finished_count = logs.iter().filter(|log| log.finished).count();
println!("Done: {} / {} total", finished_count, logs.len());
}
iter() returns immutable references. iter_mut() hands out mutable references so you can update items in place.
fn main() {
let mut progress = vec![10, 40, 70];
for percent in progress.iter_mut() {
*percent += 5;
}
println!("Updated progress: {:?}", progress);
}
Summaries with HashMap
[[hashmap|HashMap<K, V>]] stores key-value pairs. Use it to record how many notes each topic has.
use std::collections::HashMap;
fn main() {
let mut notes: HashMap<String, Vec<String>> = HashMap::new();
notes.entry("borrowing".into()).or_default().push("Multiple immutable borrows".into());
notes.entry("borrowing".into()).or_default().push("Only one mutable borrow".into());
notes.entry("enum".into()).or_default().push("Match splits state".into());
for (topic, memos) in ¬es {
println!("{}: {} notes", topic, memos.len());
}
}
The entry API inserts a new value if the key is missing, or returns the existing value if present. Use insert when you always want to overwrite; use entry plus or_default when you need "create if absent, then mutate."
HashMap Plus Enums
Bringing the Progress enum back, you can map each topic to its current status.
use std::collections::HashMap;
#[derive(Debug)]
enum Progress {
Todo,
Doing(u8),
Done,
}
fn main() {
let mut status: HashMap<String, Progress> = HashMap::new();
status.insert("borrowing".into(), Progress::Done);
status.insert("structs".into(), Progress::Doing(50));
status.insert("collections".into(), Progress::Todo);
for (topic, progress) in &status {
match progress {
Progress::Todo => println!("{}: not started", topic),
Progress::Doing(p) => println!("{}: {}% in progress", topic, p),
Progress::Done => println!("{}: finished", topic),
}
}
}
Because we iterate immutably, we cannot modify status inside that loop. Switch to iter_mut() when you truly need to update entries mid-loop, and keep the borrow rules in mind.
Iteration Cheat Sheet
for item in collectionmoves each element, so the original collection may no longer be usable afterward.for item in &collectionorcollection.iter()visits immutable references.for item in &mut collectionoriter_mut()hands out mutable references. Remember you can’t mix these with other borrows simultaneously.
In short:
- Look without touching:
&collection,iter() - Look and edit:
&mut collection,iter_mut() - Take ownership: plain
collection
Iterators also expose chainable methods (map, filter, collect), which will tie in naturally once we add closures.
Practice in CodeSandbox
The sandbox below uses CodeSandbox's Rust starter. Move the main code into src/main.rs, then compare cargo check and cargo run so you can read the compiler feedback beside the final output.
💬 댓글
이 글에 대한 의견을 남겨주세요