Function Pointers vs Closures
fn(i32) -> i32 is a function pointer - no captured environment. Closures may capture state and have unique types.
Search across all documentation pages
fn(i32) -> i32 is a function pointer - no captured environment. Closures may capture state and have unique types.
fn apply(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }
fn inc(x: i32) -> i32 { x + 1 }
fn main() {
println!("{}", apply(inc, 5));
}When to reach for this: C FFI callbacks, simple hooks without capture, and Send + Sync guarantees of fn pointers.
fn call_with<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(x) }
fn double(x: i32) -> i32 { x * 2 }
fn main() {
println!("{}", call_with(double, 3));
println!("{}", call_with(|x| x + 1, 3));
}What this demonstrates:
Fn accepts both fn item and closurefn pointerFunction items coerce to fn pointers. Closures without captures can coerce to fn pointers in some cases. FFI uses extern "C" fn.
F: Fn or Box<dyn Fn>.| Alternative | Use When | Don't Use When |
|---|---|---|
| Closure | Need capture | FFI needs extern fn |
extern "C" fn | C interop | Pure Rust only |
Box<dyn Fn> | Heterogeneous callbacks | fn pointer sufficient |
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+.
Reviewed by Chris St. John·Last updated Jul 16, 2026