Closures & Iterators
TypeScript arrow functions vs Rust closures
Section titled “TypeScript arrow functions vs Rust closures”In TypeScript, arrow functions can capture variables from their enclosing scope freely. Rust closures do the same, but the compiler tracks exactly how the closure uses its captured values and enforces ownership rules at compile time.
// TypeScript — arrow function captures 'factor' by referenceconst factor = 3;const triple = (x: number) => x * factor;console.log(triple(5)); // 15
// Closures in higher-order functionsconst numbers = [1, 2, 3, 4, 5];const doubled = numbers.map(x => x * 2);console.log(doubled); // [2, 4, 6, 8, 10]
// Capturing mutable state — counterlet count = 0;const increment = () => { count++; };increment();increment();console.log(count); // 2fn main() { // Closure captures 'factor' by reference (immutable) let factor = 3; let triple = |x| x * factor; println!("{}", triple(5)); // 15
// Iterator adapters: map, filter, collect let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<i32> = numbers.iter().map(|&x| x * 2).collect(); println!("{:?}", doubled); // [2, 4, 6, 8, 10]
// FnMut closure — captures count mutably let mut count = 0; let mut increment = || { count += 1; }; increment(); increment(); drop(increment); // release borrow so we can use count again println!("{count}"); // 2}The Fn trait hierarchy
Section titled “The Fn trait hierarchy”Rust models closure capabilities as three traits. Understanding them is important when you pass closures to functions or store them in structs.
| Trait | What it can do | TypeScript analogy |
|---|---|---|
FnOnce | Called at most once; can move captured values out | A one-shot callback that consumes its captures |
FnMut | Called multiple times; mutates captures | A stateful callback that modifies closed-over variables |
Fn | Called multiple times; only reads captures (or captures nothing) | A pure arrow function with no side effects on captured vars |
Every Fn is also FnMut; every FnMut is also FnOnce. When you take a &dyn Fn(T) -> U parameter, you accept the most restrictive requirement — the closure must be callable multiple times without mutating.
fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(f(x))}
fn main() { let double = |x| x * 2; println!("{}", apply_twice(double, 3)); // 12}Iterator adapters — lazy by default
Section titled “Iterator adapters — lazy by default”TypeScript’s .map(), .filter(), and .reduce() are eager: they allocate a new array at each step. Rust’s iterator adapters are lazy: nothing is computed until you call a consuming method like .collect(), .sum(), or .for_each(). This means a long chain of .map().filter().take() allocates no intermediate vectors.
// TypeScript — each step allocates a new arrayconst result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(x => x % 2 === 0) // new array [2,4,6,8,10] .map(x => x * x) // new array [4,16,36,64,100] .slice(0, 3); // new array [4,16,36]console.log(result);fn main() { // Rust — no intermediate allocations until collect() let result: Vec<i32> = (1..=10) .filter(|x| x % 2 == 0) // lazy .map(|x| x * x) // lazy .take(3) // lazy .collect(); // ONE allocation here println!("{:?}", result); // [4, 16, 36]
// Common adapters let sum: i32 = (1..=100).sum(); println!("sum 1..100 = {sum}");
let words = vec!["hello", "world", "rust"]; let upper: Vec<String> = words.iter() .map(|w| w.to_uppercase()) .collect(); println!("{:?}", upper);
// flat_map (like Array.flatMap) let sentences = vec!["hello world", "foo bar"]; let all_words: Vec<&str> = sentences.iter() .flat_map(|s| s.split_whitespace()) .collect(); println!("{:?}", all_words);}Try it
Section titled “Try it”fn main() { let multiplier = 3; let triple = |x| x * multiplier; println!("triple(5) = {}", triple(5));
let numbers = vec![1, 2, 3, 4, 5, 6]; let evens_doubled: Vec<i32> = numbers .iter() .filter(|&&x| x % 2 == 0) .map(|&x| x * 2) .collect(); println!("evens_doubled = {:?}", evens_doubled);
let sum: i32 = numbers.iter().sum(); println!("sum = {sum}");
let result: Vec<String> = (1..=3) .map(|x| x * x) .map(|x| format!("sq:{x}")) .collect(); println!("{:?}", result);}Compiling…