Skip to Content
Course content

27: Vectors, Strings, and HashMaps

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Wait, why are there two different types of strings?

This is usually the first wall people hit. In most languages, a string is just a string. In Rust, you've got String and &str. I like to think of it this way: String is a growable, owned bucket of characters on the heap. &str (a string slice) is just a window looking into some text that already exists somewhere else.

If you're hardcoding a label in your code, use &str. If you're taking user input or building a message dynamically, you need a String. Here is how you'll actually use them together:

let greeting_label = "Welcome, "; // This is a &str
let user_name = String::from("Alice"); // This is a String

// We combine them into a new String
let full_message = format!("{}{}", greeting_label, user_name);
println!("{}", full_message);

The key takeaway is ownership. A String owns its data, so when it goes out of scope, the memory is cleaned up. A &str is just borrowing it.

How do I update a value in a HashMap without it being a mess?

If you've come from Python or JS, you're used to just checking if a key exists and then updating it. In Rust, doing that with if map.contains_key(...) is clunky because you end up performing the lookup twice—once to check and once to insert.

You want the Entry API. It's a bit of "magic" that lets you find the spot in the map and decide what to do with it in one go. I use this constantly for things like counting word occurrences:

use std::collections::HashMap;

let mut counts = HashMap::new();
let text = "apple banana apple cherry banana apple";

for word in text.split_whitespace() {
    // This says: "Find the entry for 'word'. If it's not there, put 0 in. 
    // Then, give me a mutable reference to whatever value is there."
    let count = counts.entry(word).or_insert(0);
    *count += 1;
}

println!("{:?}", counts); // {"apple": 3, "banana": 2, "cherry": 1}

The *count += 1 part looks weird because or_insert returns a mutable reference. We have to dereference it to change the actual number inside the map.

When should I use a Vector instead of a regular array?

Arrays in Rust [T; N] have a fixed size that must be known at compile time. They're great for things that never change, like the days of the week or the coordinates of a 3D point. But in the real world, your data size is usually dynamic.

That's where Vec<T> comes in. It's basically an array that can grow. If you're building a list of high scores for a game, you don't know if you'll have 5 scores or 5,000. A Vector handles the memory reallocation for you behind the scenes.

let mut high_scores = Vec::new();

high_scores.push(1200);
high_scores.push(1500);
high_scores.push(900);

// You can access them just like an array
println!("The top score is: {}", high_scores[1]);

Just a heads up: indexing with [i] will crash (panic) your program if the index is out of bounds. If you aren't 100% sure the element exists, use .get(i), which returns an Option.




📋 Practical Task

Exercise: Build a Simple Tag Cloud Generator

Your goal is to write a program that takes a raw string of tags (separated by commas) and produces a list of tags that appear more than once, sorted by their frequency.

Requirements:

  • Create a HashMap to count how many times each tag appears in a comma-separated string (e.g., "rust,coding,rust,systems,coding,rust").
  • Filter the results: only keep tags that appear 2 or more times.
  • Push these "popular" tags into a Vec of tuples (String, i32).
  • Print the final Vector.

Bonus challenge: Try to use .split(',') and .trim() to handle tags that might have accidental spaces around them (like "rust, coding, rust").

Rating
0 0

There are no comments for now.

to be the first to leave a comment.