Skip to main content
Rust Programming
CHAPTER 14 Beginner

Collections in Rust

Updated: May 18, 2026
5 min read

# CHAPTER 14

Collections in Rust

1. Chapter Introduction

In Chapter 4, we learned about Arrays and Tuples. While useful, they are stored on the Stack, meaning their size is fixed at compile-time and cannot grow. Real-world data is unpredictable; you don't know how many users will sign up. Rust provides Collections, which store data on the Heap. This means they can grow and shrink dynamically. The three most commonly used collections are Vectors, Strings, and HashMaps.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Create, update, and iterate over Vectors (Vec<T>).
  • Deeply understand how String works under the hood (UTF-8 bytes vs chars).
  • Create, insert, and safely retrieve data from a HashMap.

3. Vectors (Vec<T>)

A Vector allows you to store more than one value in a single data structure that puts all the values next to each other in memory (like a dynamic array). Vectors can only store values of the *same type*.

Creating and Updating a Vector

rust
123456789101112
fn main() {
    // 1. Using the vec! macro (most common)
    let mut v1 = vec![1, 2, 3];

    // 2. Creating an empty Vector
    let mut v2: Vec<i32> = Vec::new();

    // Adding elements
    v2.push(5);
    v2.push(6);
    v2.push(7);
}

Reading from a Vector There are two ways to reference a value: via index, or via the .get() method.

rust
12345678910111213
fn main() {
    let v = vec![10, 20, 30, 40, 50];

    // Method 1: Indexing (Will PANIC and crash if out of bounds!)
    let third: &i32 = &v[2];
    println!("The third element is {}", third);

    // Method 2: .get() (Returns an Option<&T>. Safe from crashing!)
    match v.get(2) {
        Some(third) => println!("The third element is {}", third),
        None => println!("There is no third element."),
    }
}

4. Strings (The UTF-8 Complexity)

We used Strings earlier, but now we must confront a difficult truth: Strings in Rust are highly complex. A String is actually just a wrapper over a Vec<u8> (a vector of bytes) that guarantees the bytes represent valid UTF-8 text.

Because of UTF-8 (where an English letter takes 1 byte, but a Cyrillic letter takes 2 bytes, and an Emoji takes 4 bytes), Rust does NOT allow you to index into a String like an array (my_string[0]). Doing so could accidentally slice an emoji in half and crash the program!

Creating and Updating Strings

rust
1234567891011
fn main() {
    let mut s = String::from("foo");
    
    // Append a string slice
    s.push_str("bar"); 
    
    // Append a single character
    s.push(&#039;!&#039;); 
    
    println!("{}", s); // foobar!
}

Iterating over Strings Safely If you want to look at the individual human-readable characters, you must explicitly use the .chars() method.

rust
123
for c in "नमस्ते".chars() {
    println!("{}", c);
}

5. HashMaps

A HashMap stores data in Key-Value pairs (similar to Objects in JS or Dictionaries in Python). They are incredibly fast for looking up data.

You must bring it into scope using use std::collections::HashMap;.

Creating and Inserting Data

rust
1234567891011121314
use std::collections::HashMap;

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

    scores.insert(String::from("Blue"), 10);
    scores.insert(String::from("Yellow"), 50);

    // Overwriting a value
    scores.insert(String::from("Blue"), 25); 

    // Only insert IF the key doesn't already exist!
    scores.entry(String::from("Red")).or_insert(50);
}

Retrieving Data Because a key might not exist in the HashMap, .get() returns an Option<&V>.

rust
123456
let team_name = String::from("Blue");
// .copied() converts Option<&i32> to Option<i32>
// .unwrap_or(0) returns 0 if the key is missing
let score = scores.get(&team_name).copied().unwrap_or(0);

println!("Blue Team Score: {}", score);

6. Mini Project: Word Counter

Let's use a HashMap to count how many times each word appears in a string!
rust
1234567891011121314151617
use std::collections::HashMap;

fn main() {
    let text = "hello world wonderful world";
    let mut map = HashMap::new();

    // text.split_whitespace() creates an iterator over the words
    for word in text.split_whitespace() {
        // .entry() looks for the word.
        // .or_insert(0) creates the key with a value of 0 if it doesn't exist, and returns a MUTABLE REFERENCE to the value.
        let count = map.entry(word).or_insert(0);
        *count += 1; // Dereference the pointer and increment the value!
    }

    println!("{:?}", map); 
    // Output: {"world": 2, "hello": 1, "wonderful": 1}
}

7. Common Mistakes

  • Indexing Strings: Writing let char = mystring[0];. Rust aggressively forbids this to prevent UTF-8 corruption.
  • HashMap Ownership: Inserting a String into a HashMap *moves* ownership of that string into the HashMap. The original variable becomes invalid. (Inserting primitives like i32 just copies them).

8. Best Practices

  • Always use .get() for Vectors: Unless you are mathematically certain the index exists, avoid &v[100]. Use v.get(100) and handle the None case to prevent server panics.

9. Exercises

  1. 1. Create a Vector of integers.
  1. 2. Use a for loop to iterate through the Vector, multiplying each number by 2, and print the results.

10. MCQs with Answers

Question 1

Where are standard Collections (Vectors, Strings, HashMaps) stored in memory?

Question 2

Which macro is the fastest way to create and initialize a Vector?

Question 3

What is the danger of reading a Vector using indexing (e.g., &v[10])?

Question 4

What does v.get(10) return to prevent crashing?

Question 5

Fundamentally, what is a String in Rust?

Q6. Can you access the first letter of a String using my
string[0]? a) Yes b) No, Rust prevents this because UTF-8 characters can vary in byte size Answer: b) No, Rust prevents this to ensure UTF-8 safety.
Question 7

How do you append a word to an existing mutable String?

Question 8

Which Collection stores data in Key-Value pairs?

Question 9

If you insert a String key into a HashMap, what happens to the original String variable?

Question 10

What does the .orinsert() method do on a HashMap entry?

11. Interview Questions

  • Q: Explain why Rust does not allow integer indexing into Strings. Give an example of how UTF-8 encoding justifies this design choice.
  • Q: Contrast the memory behavior of arrays [i32; 5] vs Vectors Vec<i32>.

12. FAQs

  • Are HashMaps ordered? No. HashMaps do not preserve the order of insertion. If you iterate over a HashMap, the keys will print in a random order.

13. Summary

Collections empower your applications to handle dynamic data gracefully. Vectors provide standard list functionality, HashMaps offer lightning-fast key-value lookups, and Strings manage complex text data with uncompromising UTF-8 safety. Leveraging these tools, combined with Option handling via .get(), results in incredibly robust backend software.

14. Next Chapter Recommendation

We have used .expect() several times to crash the program when something goes wrong. In production, crashing is unacceptable. In Chapter 15: Error Handling in Rust, we will learn how to use the Result Enum to capture errors safely and recover from them gracefully.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·