Advanced Functions and Closures

Function Pointers vs Closures

Function pointers (fn) are different from closure traits (Fn, FnMut, FnOnce):

fn add_one(x: i32) -> i32 { x + 1 }

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

let answer = do_twice(add_one, 5);  // Function pointer

Key differences:

  • fn is a type, not a trait
  • Function pointers implement all closure traits
  • Useful for FFI (C functions don’t have closures)

Practical Examples

Map with function vs closure:

let list = vec![1, 2, 3];
let list2: Vec<String> = list.iter().map(ToString::to_string).collect();

Enum constructors as function pointers:

enum Status { Value(u32) }
let list: Vec<Status> = (0u32..20).map(Status::Value).collect();

Returning Closures

Simple case with impl Trait:

fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

Multiple closures require trait objects (each closure has unique type):

fn returns_closure(condition: bool) -> Box<dyn Fn(i32) -> i32> {
    if condition {
        Box::new(|x| x + 1)
    } else {
        Box::new(|x| x * 2)
    }
}

JavaScript/TypeScript Comparison

JavaScript: Functions are first-class objects with uniform representation:

function add(x) { return x + 1; }
const multiply = (x) => x * 2;
const funcs = [add, multiply];  // Same type

Rust: Functions and closures have distinct types based on captures:

fn add(x: i32) -> i32 { x + 1 }
let multiplier = 2;
let multiply = |x| x * multiplier;  // Different type due to capture

// Need trait objects for heterogeneous storage
let funcs: Vec<Box<dyn Fn(i32) -> i32>> = vec![
    Box::new(add),
    Box::new(multiply),
];

Rust’s approach enables zero-cost abstractions - closures only pay for what they capture.