🎓 A1Learner
🔍

Rust Function Pointers

A function pointer is a value that points at a plain named function. Its type is written fn, with a small f.

Name the type: fn

A function pointer type spells out what goes in and what comes out. Small f — fn is a type, not the Fn trait:

fn add_one(x: i32) -> i32 { x + 1 }
let job: fn(i32) -> i32 = add_one;
println!("job(5) = {}", job(5));

Pass a function by its name

Hand the function over with no brackets after its name. Brackets would call it. The bare name passes it:

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { f(arg) + f(arg) }
println!("do_twice = {}", do_twice(add_one, 5));

A generic Fn bound takes either

Ask for the Fn trait instead of plain fn, and callers may pass a function or a closure — both fit:

fn do_twice_generic<F: Fn(i32) -> i32>(f: F, arg: i32) -> i32 { f(arg) + f(arg) }
println!("fn in: {}", do_twice_generic(add_one, 5));
println!("closure in: {}", do_twice_generic(|x| x * 2, 5));

Box returned closures to share one type

Every closure has its own hidden type. Box<dyn Fn> wraps them into one shared type, so a Vec can hold them side by side:

fn returns_adder() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}
let handlers = vec![returns_adder(), returns_adder()];
for h in &handlers { println!("handler(5) = {}", h(5)); }

An enum variant is a function too

A variant that holds data builds a value from that data. So it can be handed to map like any function:

#[derive(Debug)]
enum Status { Value(u32), Stop }
let statuses: Vec<Status> = (0u32..3).map(Status::Value).collect();
println!("statuses = {:?}", statuses);

impl Fn hides a different type each time

Returning impl Fn promises the shape. But each function still returns its own hidden type. So two of them cannot share a Vec:

fn first() -> impl Fn(i32) -> i32 { |x| x + 1 }
fn second() -> impl Fn(i32) -> i32 { |x| x + 2 }
// let both = vec![first(), second()]; // error E0308: two different hidden types

Example

fn add_one(x: i32) -> i32 { x + 1 }
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { f(arg) + f(arg) }
fn returns_closure() -> Box<dyn Fn(i32) -> i32> { Box::new(|x| x + 1) }
fn returns_starting(init: i32) -> Box<dyn Fn(i32) -> i32> { Box::new(move |x| x + init) }
#[derive(Debug)]
enum Status { Value(u32), Stop }
fn main() {
    println!("answer = {}", do_twice(add_one, 5));
    let handlers = vec![returns_closure(), returns_starting(100)];
    for handler in handlers { println!("{}", handler(5)); }
    let statuses: Vec<Status> = (0u32..3).map(Status::Value).collect();
    println!("statuses = {:?}", statuses);
}

Key points

  • A function pointer implements Fn, FnMut and FnOnce, so it fits anywhere a closure is accepted
  • Ask for plain fn only when the other side has no closures — C functions are the classic case
  • Return a closure with -> impl Fn(i32) -> i32 for one fixed closure shape

In this example, do_twice takes an fn pointer and calls it twice on 5: 6 + 6 prints answer = 12. The two returned closures have different hidden types. So both functions return Box<dyn Fn>. That gives them one shared type. The Vec then runs them on 5. It prints 6 and 105. Last, Status::Value is used as a function: map turns 0, 1 and 2 into [Value(0), Value(1), Value(2)].

Note

fn is a type you can name and store

Fn is a trait that closures, function pointers and a few other callables all share

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why return Box<dyn Fn> instead of impl Fn when collecting closures in a Vec?