Skip to Content
Course content

110: The Iterator Trait Methods: map, filter, fold

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

I've always found that the best way to understand Iterators in Rust isn't by reading the documentation for the Iterator trait, but by trying to process a chunk of data and realizing how tedious for loops can get. Let's look at a real-world scenario: imagine we have a list of shopping transactions, and we want to find the total cost of just the "Electronics" items, but only after applying a 10% discount to each of them.

struct Transaction {
    item: String,
    category: String,
    price: f64,
}

fn main() {
    let history = vec![
        Transaction { item: "Laptop".to_string(), category: "Electronics".to_string(), price: 1000.0 },
        Transaction { item: "Apple".to_string(), category: "Grocery".to_string(), price: 1.0 },
        Transaction { item: "Mouse".to_string(), category: "Electronics".to_string(), price: 50.0 },
        Transaction { item: "Bread".to_string(), category: "Grocery".to_string(), price: 2.5 },
    ];
}

Sifting through the noise

My first instinct is to just loop through the history vector and add things up. But that feels clunky. I want to describe what I want, not how to move the pointer. Let's start by isolating the electronics. I'll use filter.

let electronics = history.iter().filter(|t| t.category == "Electronics");
println!("Electronics found: {:?}", electronics.count());

Wait, a quick note here: filter takes a closure that returns a boolean. If it's true, the element stays; if false, it's tossed. One thing that tripped me up when I first started with Rust is that filter gives me a reference to the item (in this case, &Transaction), which is why I can just check the category. It's efficient—no cloning happens here.

Changing the values on the fly

Now I have a stream of electronics, but I can't just sum them up yet because I need to apply that 10% discount. I don't want to mutate the original history vector—that's a recipe for bugs. Instead, I'll use map to transform the data as it flows through the iterator.

let discounted_prices = history.iter()
    .filter(|t| t.category == "Electronics")
    .map(|t| t.price * 0.9);

Here's the "aha!" moment: if I try to print discounted_prices right now, I won't see the numbers. Why? Because iterators in Rust are lazy. They are essentially a set of instructions. map doesn't actually do the multiplication yet; it just says, "Whenever someone eventually asks for a value, I'll multiply it by 0.9 first." This is incredibly powerful for performance because Rust can optimize the entire chain into a single pass over the data.

Collapsing the stream into a result

I have a stream of discounted prices, but I need a single f64 representing the total. I could use .sum(), which is a convenient shorthand, but I want to show you fold. fold is the "Swiss Army Knife" of iterator methods. It takes an initial value (an accumulator) and a closure to combine that accumulator with each element.

let total = history.iter()
    .filter(|t| t.category == "Electronics")
    .map(|t| t.price * 0.9)
    .fold(0.0, |acc, price| acc + price);

println!("The total discounted electronics cost is: ${}", total);

Let's break down that fold(0.0, |acc, price| acc + price). The 0.0 is where we start. The first time the iterator runs, acc is 0.0 and price is the price of the Laptop (900.0). The result (900.0) becomes the acc for the next item (the Mouse, 45.0). It "folds" the entire list down into one final value.

I personally prefer fold over sum when the logic gets more complex—like if I wanted to keep track of both the total price and the number of items processed in a single pass by folding into a tuple. It's much more flexible.




📋 Practical Task

Building a Premium Subscription Revenue Calculator

You are tasked with calculating the total monthly revenue for a software service. You have a list of users, each with a plan (either "Basic", "Premium", or "Enterprise") and a monthly_fee. However, some users have a discount_code (a boolean) that gives them 20% off their fee.

Write a program that does the following using a single iterator chain:

  • Filters the list to include only users on the "Premium" or "Enterprise" plans.
  • Maps the fees so that users with a discount_code have their fee reduced by 20%, while others keep the full fee.
  • Uses fold to calculate the final total revenue of these specific users.

Start with this data structure:

struct User {
    name: String,
    plan: String,
    monthly_fee: f64,
    discount_code: bool,
}

let users = vec![
    User { name: "Alice".to_string(), plan: "Premium".to_string(), monthly_fee: 20.0, discount_code: false },
    User { name: "Bob".to_string(), plan: "Basic".to_string(), monthly_fee: 10.0, discount_code: false },
    User { name: "Charlie".to_string(), plan: "Enterprise".to_string(), monthly_fee: 100.0, discount_code: true },
    User { name: "Dave".to_string(), plan: "Premium".to_string(), monthly_fee: 20.0, discount_code: true },
];
Rating
0 0

There are no comments for now.

to be the first to leave a comment.