Question
Can a function be passed as a parameter in Rust? If so, what is the correct syntax, and if not, what is the best alternative?
For example, I know a function can be assigned to a function pointer variable like this:
fn example() {
let fun: fn(i32) -> i32;
fun = fun_test;
fun(5_i32);
}
fn fun_test(value: i32) -> i32 {
println!("{}", value);
value
}
But I want to pass a function into another function, something conceptually like this:
fn fun_test(value: i32, /* some function type here */) -> i32 {
println!("{}", value);
value
}
What is the correct way to do this in Rust?
Short Answer
By the end of this page, you will understand how Rust lets you pass behavior into functions using function pointers and closures. You will learn the syntax for fn parameter types, when to use generics with Fn, FnMut, or FnOnce, and how this pattern is used in real Rust code.
Concept
In Rust, you can pass a function as a parameter.
There are two closely related ideas:
- Function pointers: a parameter typed as
fn(...) -> ... - Closures: anonymous functions that can capture values from their environment
A named function like this:
fn double(x: i32) -> i32 {
x * 2
}
can be passed to another function if the parameter type matches:
fn apply(value: i32, f: fn(i32) -> i32) -> i32 {
f(value)
}
This works because double has the type fn(i32) -> i32.
Why this matters:
- It lets you reuse logic without hardcoding behavior.
- It is useful for callbacks, transformations, validation, and configuration.
- It is the basis for patterns like
map, filtering, custom sorting, and event handling.
Mental Model
Think of a function parameter as handing someone a tool.
- A normal
i32parameter is like giving someone a number. - A function parameter is like giving someone a machine they can run later.
For example:
5is datadoubleis an instruction for what to do with data
So instead of saying:
Always multiply by 2
you can say:
Here is a function. Use it on the value.
That makes your code more reusable. One function can work with many behaviors, depending on which function you pass in.
Syntax and Examples
Passing a named function
If you want to accept a regular function, use the fn type:
fn apply(value: i32, operation: fn(i32) -> i32) -> i32 {
operation(value)
}
fn square(x: i32) -> i32 {
x * x
}
fn main() {
let result = apply(4, square);
println!("{}", result); // 16
}
How it works
operation: fn(i32) -> i32means:operationis a function- it takes one
i32 - it returns one
i32
squarematches that exact signature, so it can be passed in.
Step by Step Execution
Consider this code:
fn apply(value: i32, op: fn(i32) -> i32) -> i32 {
op(value)
}
fn increment(x: i32) -> i32 {
x + 1
}
fn main() {
let result = apply(5, increment);
println!("{}", result);
}
Step by step
main()starts running.apply(5, increment)is called.- The value
5is passed intoapplyasvalue. - The function
incrementis passed intoapplyasop.
Real World Use Cases
Passing functions or closures is useful whenever code needs custom behavior.
Common use cases
- Transforming data
- Apply a function to each item in a list
- Validation
- Pass different validation rules into a shared function
- Callbacks
- Run user-provided logic after an operation completes
- Sorting and filtering
- Decide how items are compared or selected
- Testing
- Pass mock behavior into functions instead of using real dependencies
Example: reusable validator
fn check_value<F>(value: i32, rule: F) -> bool
where
F: Fn(i32) -> bool,
{
rule(value)
}
fn main() {
let is_positive = check_value(10, |x| x > 0);
let is_even = check_value(, |x| x % == );
(, is_positive);
(, is_even);
}
Real Codebase Usage
In real Rust codebases, developers usually choose between function pointers and closure traits based on flexibility.
Common patterns
1. Simple callback with a function pointer
Used when only a plain function is needed.
fn run_callback(callback: fn()) {
callback();
}
This is simple and clear, but less flexible.
2. Generic closure parameter
Used when the caller may pass either a named function or a closure.
fn process<F>(value: i32, f: F) -> i32
where
F: Fn(i32) -> i32,
{
f(value)
}
This is very common in application code and libraries.
3. Guard clauses before calling behavior
fn apply_if_positive<F>(value: i32, f: F) -> Option<i32>
where
F: () ,
{
value < {
;
}
((value))
}
Common Mistakes
1. Using the wrong type syntax
Broken:
fn apply(value: i32, operation(i32) -> i32) -> i32 {
operation(value)
}
Correct:
fn apply(value: i32, operation: fn(i32) -> i32) -> i32 {
operation(value)
}
You must include the parameter name, followed by :, then the type.
2. Confusing fn with Fn
fn(i32) -> i32is a function pointer typeFn(i32) -> i32is a trait bound for closures and callable values
Broken:
Comparisons
| Approach | Syntax | Accepts named functions | Accepts closures | Best for |
|---|---|---|---|---|
| Function pointer | fn(i32) -> i32 | Yes | Only non-capturing closures that can coerce | Simple fixed callbacks |
Generic with Fn | F: Fn(i32) -> i32 | Yes | Yes | Most reusable read-only callable behavior |
Generic with FnMut | F: FnMut(i32) -> i32 | Yes | Yes | Closures that modify captured state |
Generic with FnOnce |
Cheat Sheet
Quick syntax
Accept a function pointer
fn apply(value: i32, f: fn(i32) -> i32) -> i32 {
f(value)
}
Pass a named function
fn square(x: i32) -> i32 {
x * x
}
let result = apply(4, square);
Accept functions and closures
fn apply<F>(value: i32, f: F) -> i32
where
F: Fn(i32) -> i32,
{
f(value)
}
Pass a closure
let = (, |x| x + );
FAQ
Can you pass a function as an argument in Rust?
Yes. Use a parameter type like fn(i32) -> i32 for a plain function pointer, or use a generic bound like F: Fn(i32) -> i32 to also support closures.
What is the difference between fn and Fn in Rust?
fn is a concrete function pointer type. Fn is a trait used for closures and other callable values. Fn is usually more flexible.
Can closures be passed where a fn is expected?
Only some closures, such as non-capturing closures, can coerce to fn. Closures that capture variables usually require Fn, FnMut, or FnOnce.
When should I use FnMut instead of Fn?
Use FnMut when the closure needs to modify captured state, such as incrementing a counter.
Why does my function not match fn(i32) -> i32?
Mini Project
Description
Build a small Rust program that applies different operations to a number by passing functions and closures into a reusable helper function. This demonstrates how Rust can treat behavior as a parameter, which is useful for callbacks, transformations, and configurable business logic.
Goal
Create a reusable function that can apply different operations, such as doubling, squaring, and adding a captured value, to an input number.
Requirements
- Create one function that accepts a number and a callable operation.
- Pass at least one named function into it.
- Pass at least one closure into it.
- Print the result of each operation.
- Include one closure that captures a local variable.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Can a Struct Extend Another Struct in Rust? Composition vs Inheritance
Learn how Rust handles struct reuse without inheritance, using composition, traits, and wrapper structs with practical examples.