[Rust Series 10] Working with Collections

한국어 버전

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 &notes {
        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

  1. for item in collection moves each element, so the original collection may no longer be usable afterward.
  2. for item in &collection or collection.iter() visits immutable references.
  3. for item in &mut collection or iter_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.

Live Practice

Rust Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Rust startercargoterminal
  1. Fork the starter and open src/main.rs
  2. Paste the lesson code and run cargo check plus cargo run in order
  3. Change types, values, or borrowing flow and compare the compiler feedback with the output

Rust practice here is mainly terminal-driven rather than browser-preview driven. Lessons that need multiple files or extra crates may require a bit more setup inside the starter.

Try It Yourself

  • Write a function that pulls only finished items from Vec<StudyLog> into a new vector.
  • Use a HashMap plus slices (&str) to print each topic’s memo list without cloning strings.
  • Replace a for loop with iter().enumerate() to print progress values with indices.

Wrap-Up

Collections are where programs manipulate real data. Vec and HashMap cover most everyday needs, and once you stick to the borrowing plus iterator patterns, you can traverse and mutate large datasets safely. In the next part we’ll build on these collections for error handling and module structure as the project scales.

💬 댓글

이 글에 대한 의견을 남겨주세요