Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
110: The Iterator Trait Methods: map, filter, fold
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_codehave their fee reduced by 20%, while others keep the full fee. - Uses
foldto 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 },
];
There are no comments for now.