Higher-Order Functions
Functions that take functions (closures) as arguments or return them enable composition and reusable control patterns.
Recipe
fn twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(f(x))
}
fn main() {
println!("{}", twice(|n| n + 1, 0));
}When to reach for this: map/filter style utilities, middleware, and custom iteration.
Working Example
fn retry<F: FnMut() -> bool>(mut f: F, max: u32) -> bool {
for _ in 0..max {
if f() { return true; }
}
false
}
fn main() {
let mut attempts = 0;
let ok = retry(|| { attempts += 1; attempts >= 3 }, 5);
println!("{ok} {attempts}");
}What this demonstrates:
FnMutfor mutating attempt counter in closure- Higher-order control flow (retry)
- Closures as strategy parameter
Deep Dive
Iterator adapters are higher-order: map, filter take closures. Compose small HOFs for clarity.
Gotchas
- Over-abstracting HOFs - Hard to read. Fix: Name operations clearly.
- Fn vs FnMut bound too tight - Compile failures. Fix: Relax to
FnMutif needed. - Recursion depth - HOF calling itself via closure - stack limits.
- Allocation in returned closure - Hidden cost in factory HOFs.
- Testing HOFs - Pass simple closures or mock fns.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Trait object callback | Runtime plugins | Static generics work |
| Inline for loop | Simple one-off | Reused pattern |
| Iterator adapters | Collection transforms | Imperative retry |
FAQs
hof vs method?
Free function accepting closure vs method on struct.compose?
Manual |f(g(x))| or itertools.currying?
Not native - closures `move |y| x + y`.partial application?
Closure fixing args.async hof?
Generic over async closures evolving.performance?
Monomorphized - usually inlined.no_std?
Same patterns in core.test doubles?
Pass closure recording calls.middleware axum?
Layers are higher-order service transforms.iterator fold?
HOF reducing collection.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.