Advanced Type Features

Function Pointer Types

A function pointer type—written fn(T1, T2) -> Ret (with lowercase fn)—is a primitive, pointer-sized (8 bytes on 64-bit targets) value that stores the code address of a compiled function. Unlike the closure traits (Fn, FnMut, FnOnce), fn(...) is a concrete type, not a trait.

Function Item Types vs. Function Pointer Types

Every named function or tuple-struct/enum-variant constructor in Rust actually has its own unique, zero-sized function item type (fn(i32) -> i32 {double}), which coerces implicitly to the general function pointer type fn(i32) -> i32:

Function Item Type (Zero-Sized Compile-Time Identity)
  `let f = double;`
  Type: `fn(i32) -> i32 {double}`
  Size: `0` bytes (calls are statically dispatched and inlined!)
        |
        | Implicit function-item-to-fn-pointer coercion
        v
Function Pointer Type (Runtime Code Address)
  `let fp: fn(i32) -> i32 = double;`
  Type: `fn(i32) -> i32`
  Size: `8` bytes (indirect call via register address)
fn add_one(x: i32) -> i32 {
    x + 1
}

fn square(x: i32) -> i32 {
    x * x
}

#[derive(Debug)]
struct Meter(i32);

#[derive(Debug)]
enum Status {
    Code(i32),
}

fn main() {
    let fn_item = add_one;
    let fn_ptr: fn(i32) -> i32 = add_one;

    println!("size of fn_item (zero-sized): {} bytes", size_of_val(&fn_item));
    println!("size of fn_ptr  (address):    {} bytes", size_of_val(&fn_ptr));

    // Homogeneous slice of function pointers (including a non-capturing closure!):
    let ops: [fn(i32) -> i32; 3] = [add_one, square, |x| x * 10];
    let mut val = 3;
    for op in ops {
        val = op(val);
    }
    println!("chained ops on 3: {}", val);

    // Tuple struct and enum variant constructors also coerce to `fn(...)` pointers!
    let make_meter: fn(i32) -> Meter = Meter;
    let make_status: fn(i32) -> Status = Status::Code;
    println!("{:?}, {:?}", make_meter(5), make_status(200));
}
size of fn_item (zero-sized): 0 bytes
size of fn_ptr  (address):    8 bytes
chained ops on 3: 160
Meter(5), Code(200)

Trait Implementations on fn(...) Pointers

Every safe function pointer type fn(Args...) -> Ret automatically implements:

  • Fn, FnMut, and FnOnce (so you can pass a fn(...) pointer anywhere a generic impl Fn(...) is expected!).
  • Copy, Clone, Send, and Sync.
  • PartialEq, Eq, PartialOrd, Ord, Hash, Debug, and Pointer ({:p}).
  • Niche optimization: Option<fn(i32) -> i32> is guaranteed to occupy 8 bytes (None is 0x0).

Unsafe and ABI-Specific Function Pointer Types

Function pointer types can carry unsafe and extern "ABI" qualifiers as part of their static type:

Function Pointer Type Can Coerce From Safe fn()? Can Be Called Without unsafe?
fn(i32) -> i32 Yes Yes
unsafe fn(i32) -> i32 Yes (fn coerces to unsafe fn, never vice versa!) No (requires unsafe {} block)
extern "C" fn(i32) -> i32 Only from extern "C" fn items or stateless closures Yes
unsafe extern "C" fn(i32) -> i32 From safe or unsafe extern "C" fn No (requires unsafe {} block)