TechLead
Lesson 9 of 28
5 min read
Rust

Collections: Vec, HashMap, String

Learn Rust collections including Vec, HashMap, HashSet, String vs &str, the entry API, BTreeMap, and VecDeque for efficient data handling.

Vec<T> — Dynamic Arrays

Vec<T> is Rust's growable, heap-allocated array. It's the most commonly used collection, similar to ArrayList in Java or std::vector in C++.

fn main() {
    // Creating vectors
    let mut v: Vec = Vec::new();
    let v2 = vec![1, 2, 3, 4, 5]; // vec! macro

    // Adding elements
    v.push(1);
    v.push(2);
    v.push(3);

    // Accessing elements
    let third = &v[2];           // Panics if out of bounds
    let maybe = v.get(2);        // Returns Option<&T>
    println!("Third: {third}, Maybe: {:?}", maybe);

    // Iterating
    for val in &v {
        print!("{val} ");
    }
    println!();

    // Mutable iteration
    for val in &mut v {
        *val *= 2;
    }
    println!("{:?}", v); // [2, 4, 6]

    // Useful methods
    let mut nums = vec![3, 1, 4, 1, 5, 9, 2, 6];
    nums.sort();
    nums.dedup();                // Remove consecutive duplicates
    println!("Sorted unique: {:?}", nums);
    println!("Contains 5? {}", nums.contains(&5));
    println!("Length: {}, Capacity: {}", nums.len(), nums.capacity());

    // Removing elements
    let last = nums.pop();       // Remove last
    let second = nums.remove(1); // Remove at index (shifts elements)
    nums.retain(|&x| x > 3);    // Keep only elements > 3
    println!("After removes: {:?}", nums);

    // Slices
    let slice = &v2[1..3];       // [2, 3]
    println!("Slice: {:?}", slice);
}

String vs &str

String Types in Rust

  • String: Owned, heap-allocated, growable UTF-8 string. Use when you need to own or modify the string.
  • &str: Borrowed string slice. A view into a String or string literal. Use in function parameters.
fn main() {
    // String (owned)
    let mut s = String::from("Hello");
    s.push_str(", world");
    s.push('!');
    println!("{s}");

    // &str (borrowed)
    let greeting: &str = "Hello"; // String literal is &'static str
    let slice: &str = &s[0..5];   // Slice of a String

    // Conversions
    let owned: String = greeting.to_string();
    let also_owned: String = String::from("hello");
    let borrowed: &str = &owned;

    // String concatenation
    let hello = String::from("Hello");
    let world = String::from(" World");
    let combined = hello + &world; // hello is moved!
    // Use format! to avoid moves:
    let a = String::from("tic");
    let b = String::from("tac");
    let c = String::from("toe");
    let result = format!("{a}-{b}-{c}"); // No moves!
    println!("{result}");

    // Iterating over characters (Rust strings are UTF-8)
    for c in "hello".chars() {
        print!("{c} ");
    }
    println!();

    // String methods
    let s = String::from("  Hello, Rust!  ");
    println!("Trimmed: '{}'", s.trim());
    println!("Uppercase: {}", s.to_uppercase());
    println!("Contains Rust? {}", s.contains("Rust"));
    println!("Replace: {}", s.replace("Rust", "World"));

    let parts: Vec<&str> = "a,b,c".split(',').collect();
    println!("Split: {:?}", parts);
}

HashMap<K, V>

use std::collections::HashMap;

fn main() {
    let mut scores: HashMap = HashMap::new();

    // Insert
    scores.insert(String::from("Alice"), 100);
    scores.insert(String::from("Bob"), 85);
    scores.insert(String::from("Charlie"), 92);

    // Access
    let alice_score = scores.get("Alice");       // Option<&i32>
    println!("Alice: {:?}", alice_score);

    // Entry API — insert only if key doesn't exist
    scores.entry(String::from("Alice")).or_insert(0);   // Already exists
    scores.entry(String::from("Dave")).or_insert(78);    // Inserted

    // Entry API — update based on old value
    let text = "hello world hello rust hello";
    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;
    }
    println!("Word counts: {:?}", word_count);

    // Iterating
    for (name, score) in &scores {
        println!("{name}: {score}");
    }

    // Useful methods
    println!("Contains Alice? {}", scores.contains_key("Alice"));
    println!("Length: {}", scores.len());
    scores.remove("Bob");

    // Collect into HashMap
    let teams = vec!["Red", "Blue", "Green"];
    let initial_scores = vec![0, 0, 0];
    let team_scores: HashMap<_, _> = teams
        .into_iter()
        .zip(initial_scores.into_iter())
        .collect();
    println!("{:?}", team_scores);
}

HashSet, BTreeMap, and VecDeque

use std::collections::{HashSet, BTreeMap, VecDeque};

fn main() {
    // HashSet — unique values, fast lookup
    let mut fruits: HashSet<&str> = HashSet::new();
    fruits.insert("apple");
    fruits.insert("banana");
    fruits.insert("apple"); // Duplicate ignored
    println!("Fruits: {:?}", fruits); // {"apple", "banana"}

    let set_a: HashSet = [1, 2, 3, 4].into();
    let set_b: HashSet = [3, 4, 5, 6].into();
    println!("Union: {:?}", &set_a | &set_b);
    println!("Intersection: {:?}", &set_a & &set_b);
    println!("Difference: {:?}", &set_a - &set_b);

    // BTreeMap — sorted keys, O(log n) operations
    let mut btree = BTreeMap::new();
    btree.insert("charlie", 3);
    btree.insert("alice", 1);
    btree.insert("bob", 2);
    // Always iterates in key order:
    for (k, v) in &btree {
        println!("{k}: {v}"); // alice, bob, charlie
    }

    // VecDeque — double-ended queue
    let mut deque: VecDeque = VecDeque::new();
    deque.push_back(1);
    deque.push_back(2);
    deque.push_front(0);
    println!("Deque: {:?}", deque); // [0, 1, 2]
    println!("Pop front: {:?}", deque.pop_front()); // Some(0)
    println!("Pop back: {:?}", deque.pop_back());   // Some(2)
}

Key Takeaways

  • Vec<T> is the go-to growable array — use it for ordered collections
  • String is owned and growable; &str is a borrowed view — prefer &str in function parameters
  • HashMap's entry API elegantly handles insert-or-update patterns
  • HashSet for unique values, BTreeMap for sorted keys, VecDeque for double-ended access
  • ✅ All collections are generic and integrate with Rust's ownership and borrowing system

Continue Learning