Higher-Order Functions
In this page:
fn name<F>(f: F) -> ReturnType
where
F: Fn(ParamType) -> ReturnType,
{
f(value)
}
Passing a Closure as a Parameter
A function can accept a closure as a parameter by specifying a trait bound like Fn(i32) -> i32, which describes any callable that takes an i32 and returns an i32.
Example: Passing a Closure as a Parameter
fn apply_twice<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
f(f(x))
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `result`, set to `apply_twice(|n| n + 3, 10)`
let result = apply_twice(|n| n + 3, 10);
// Print "{}" to the console, substituting in `result` (with a trailing newline)
println!("{}", result);
}
Login to try C/C++/Java/PHP code in the editor
Using map with a Closure
The standard library's .map() method on iterators is a higher-order function: it takes a closure and applies it to every element, producing a new iterator of transformed values.
Example: Using map with a Closure
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `numbers`, set to `vec![1, 2, 3]`
let numbers = vec![1, 2, 3];
// Declare an immutable variable `doubled` (a `Vec<i32>`), set to `numbers.iter().map(|n| n * 2).collect()`
let doubled: Vec<i32> = numbers.iter().map(|n| n * 2).collect();
// Print "{:?}" to the console, substituting in `doubled` (with a trailing newline)
println!("{:?}", doubled);
}
Login to try C/C++/Java/PHP code in the editor
Returning a Closure
Functions can also return closures, typically boxed behind Box<dyn Fn(...) -> ...> since closures have unique, unnamed types that need to be captured in a trait object to return.
Example: Returning a Closure
// Define the function `make_adder` taking `amount` and returning a `Box<dyn Fn(i32) -> i32>`
fn make_adder(amount: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |x| x + amount)
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `add_five`, set to `make_adder(5)`
let add_five = make_adder(5);
// Print "{}" to the console, substituting in `add_five(10)` (with a trailing newline)
println!("{}", add_five(10));
}
Login to try C/C++/Java/PHP code in the editor
Passing a Named Function Instead of a Closure
Because plain functions and closures without captures share a compatible shape, you can pass a named function directly wherever a matching closure type is expected.
Example: Passing a Named Function Instead of a Closure
// Define the function `triple` taking `x` and returning an integer
fn triple(x: i32) -> i32 {
x * 3
}
// Define the function `apply` taking `f`, `value` and returning an integer
fn apply(f: fn(i32) -> i32, value: i32) -> i32 {
f(value)
}
// Entry point: execution of the program starts here
fn main() {
// Print "{}" to the console, substituting in `apply(triple, 4)` (with a trailing newline)
println!("{}", apply(triple, 4));
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting to specify the closure trait bound (
Fn,FnMut, orFnOnce) when writing a function that accepts a closure parameter. - Assuming closures and function pointers are exactly interchangeable in every generic context, when their trait bounds differ subtly.
- Overcomplicating simple logic with higher-order functions when a plain loop would be clearer for beginners reading the code.
- A function that accepts a closure or another function as a parameter is called a higher-order function.
- The
Fntrait bound describes a closure that can be called repeatedly without consuming captured variables. - Standard library methods like
.map()and.filter()are higher-order functions taking closures. - Function pointers (
fn(i32) -> i32) can also be passed like values, similar to closures.
Chapter Quiz — Complete all 6 topics to unlock
0/6 topics done
Complete these topics first: