Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A function pointer in Rust is a primitive, statically sized type that represents a memory address pointing to a function’s machine code. Denoted by the lowercase fn keyword, it implements the Sized trait, has a fixed size known at compile time (occupying the exact same memory footprint as a usize), and does not capture any surrounding lexical environment.

Syntax and Type Signature

The type signature of a function pointer consists of the fn keyword, a parenthesized list of parameter types, and an optional return type.
// Type signature for a function taking an i32 and returning a bool
let ptr: fn(i32) -> bool;

// Type signature for a function taking no arguments and returning nothing (unit type)
let ptr_unit: fn();

Safe and Unsafe Function Pointers

In Rust, function pointers distinguish between safe and unsafe contexts at the type level. An unsafe fn is a completely distinct type from a safe fn pointer. Calling an unsafe fn pointer requires an unsafe block. Safe function pointers can implicitly coerce into unsafe function pointers, but the reverse is strictly prohibited.
fn safe_func() {}
unsafe fn unsafe_func() {}

let safe_ptr: fn() = safe_func;
let unsafe_ptr: unsafe fn() = unsafe_func;

// Safe function pointers coerce to unsafe function pointers
let coerced_ptr: unsafe fn() = safe_ptr;

// ERROR: Unsafe function pointers cannot coerce to safe function pointers
// let invalid_ptr: fn() = unsafe_ptr;

Function Items vs. Function Pointers

In Rust, defining a function does not immediately create a function pointer. Instead, it creates a Function Item. A function item is a unique, zero-sized type (ZST) that uniquely identifies that specific function. Because it is zero-sized, passing a function item around incurs no runtime overhead. However, function items automatically coerce into function pointers (fn) when required by the type context.
fn add_one(x: i32) -> i32 { x + 1 }

// `f_item` is a zero-sized function item type specific to `add_one`.
let f_item = add_one; 

// `f_ptr` is explicitly typed as a function pointer. 
// The function item implicitly coerces to a function pointer.
let f_ptr: fn(i32) -> i32 = add_one; 

// Explicit casting is also valid
let f_ptr_cast = add_one as fn(i32) -> i32;

Relationship with Closures

Function pointers are distinct from closures, but they interact closely with Rust’s closure traits (Fn, FnMut, and FnOnce).
  1. Trait Implementation: The fn type automatically implements all three closure traits. Any generic function bounded by impl Fn(), impl FnMut(), or impl FnOnce() will accept a function pointer.
  2. Closure Coercion: A closure that does not capture any variables from its environment can be coerced into a function pointer. A capturing closure cannot.
// Non-capturing closure coerces to a function pointer
let non_capturing: fn(i32) -> i32 = |x| x * 2;

let y = 10;
// ERROR: Capturing closure cannot be coerced to a function pointer
// let capturing: fn(i32) -> i32 = |x| x * y; 

ABI and Foreign Function Interface (FFI)

Function pointers can specify an Application Binary Interface (ABI) string to dictate how arguments and return values are passed at the assembly level. This is strictly required when interfacing with code written in other languages, such as C. Because calling foreign code often falls outside Rust’s safety guarantees, FFI function pointers are typically marked unsafe.
// A safe function pointer using the standard C ABI
let c_ptr: extern "C" fn(i32) -> i32;

// An unsafe function pointer using the C ABI (the standard for FFI)
let unsafe_c_ptr: unsafe extern "C" fn(i32) -> i32;

// A function pointer using the "stdcall" ABI (often used in Windows APIs)
let win_ptr: extern "stdcall" fn() -> i32;
If no ABI is specified, the function pointer defaults to the "Rust" ABI.

Memory Layout and Null-Pointer Optimization

Function pointers in Rust are strictly non-nullable; they must always point to a valid function address. Because of this guarantee, the Rust compiler applies the null-pointer optimization when a function pointer is wrapped in an Option.
use std::mem::size_of;

// Both types occupy the exact same amount of memory (e.g., 8 bytes on a 64-bit system).
// The `None` variant is represented by a null memory address (0x0).
assert_eq!(
    size_of::<fn()>(),
    size_of::<Option<fn()>>()
);
Master Rust with Deep Grasping Methodology!Learn More