Pybites Logo Rust Platform

Iterator Chaining

Medium +3 pts

🎯 In Python, you can chain operations in a list comprehension or with itertools:

# Filter and transform in one comprehension
result = [x * 2 for x in data if x > 0]

# Multiple steps with itertools
from itertools import chain
flat = list(chain.from_iterable(nested_lists))
longest = sorted(words, key=len)[-1]

Rust takes the chaining idea further — every iterator adapter returns a new iterator, so you can build multi-step pipelines that read top-to-bottom, like Unix pipes. And because each step is lazy, Rust fuses them into efficient single-pass execution.

Building pipelines

Each method returns a new iterator that wraps the previous one. Nothing executes until a consuming adapter (.collect(), .sum(), etc.) drives the chain:

let result: Vec<i32> = data.iter()
    .filter(|&&x| x > 0)      // keep positives
    .map(|&x| x * 2)           // double each
    .collect();                 // materialize

This is equivalent to [x * 2 for x in data if x > 0] in Python — but each step is a separate, composable method. You can add or remove steps without restructuring the expression.

.take(n) and .skip(n) — lazy slicing

Python uses slicing: data[:3], data[2:]. Rust's equivalent uses lazy iterator adapters:

let first_three: Vec<_> = data.iter().take(3).collect();
let skip_two: Vec<_> = data.iter().skip(2).collect();
let middle: Vec<_> = data.iter().skip(2).take(3).collect();

The key advantage: .take() stops consuming the iterator as soon as it has enough elements. On a large or infinite iterator, this is essential — slicing requires the whole collection to exist in memory.

.flatten() — unwrap nested structures

.flatten() takes an iterator of iterators (or iterable things) and produces a single flat iterator. It's Python's itertools.chain.from_iterable:

# Python
from itertools import chain
flat = list(chain.from_iterable([[1, 2], [3, 4]]))  # [1, 2, 3, 4]
// Rust
let flat: Vec<i32> = vec![vec![1, 2], vec![3, 4]]
    .into_iter()
    .flatten()
    .collect();
// [1, 2, 3, 4]

.enumerate() — index access

Just like Python's enumerate():

for (i, value) in data.iter().enumerate() {
    println!("{i}: {value}");
}

Clamping values with .max() and .min()

Rust's integer types have .max() and .min() methods for bounding values:

let floored = (score - penalty).max(0);  // never goes below 0

This is like Python's max(score - penalty, 0), but called as a method on the value. You can chain both for a range: value.max(0).min(100) keeps a value between 0 and 100.

Custom sorting

Rust's .sort() sorts ascending by default. For descending or custom ordering, use .sort_by():

let mut words = vec!["hi", "hello", "hey"];
words.sort_by_key(|w| w.len());  // by length: ["hi", "hey", "hello"]

The closure returns a key to sort by. For full control, .sort_by() takes a closure that receives two elements and returns an Ordering (Less, Equal, Greater) — swapping the arguments reverses the order. Both sort in place and require a mutable Vec.

Login to see the full task and start coding.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise