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, andFnOnce(so you can pass afn(...)pointer anywhere a genericimpl Fn(...)is expected!).Copy,Clone,Send, andSync.PartialEq,Eq,PartialOrd,Ord,Hash,Debug, andPointer({:p}).- Niche optimization:
Option<fn(i32) -> i32>is guaranteed to occupy8bytes (Noneis0x0).
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) |