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 closure in Rust implementing the Fn trait is an anonymous function whose body only requires shared, immutable access (&self) to its captured environment. Because it neither mutates nor consumes its captured variables, an Fn closure can be invoked multiple times and can be safely shared across threads if its captured data permits. The compiler implements the Fn trait based on how the closure uses its captured variables, not by how it captures them.

The Trait Definition

Under the hood, Fn is a trait defined in the standard library. Its defining characteristic is that its call method takes &self (an immutable reference to the closure’s environment).
pub trait Fn<Args>: FnMut<Args> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
Because Fn requires &self, the compiler guarantees that invoking the closure will not alter the state of any captured variables. Furthermore, the trait hierarchy dictates that any type implementing Fn automatically implements FnMut (which takes &mut self) and FnOnce (which takes self by value). The Output associated type is actually defined on the base FnOnce trait.

Syntax and Type Inference

Closures are defined using pipe characters | for parameters, followed by the expression or block. The compiler automatically infers the Fn trait if the closure’s body only requires read-only access to the environment.
let x = 10;

// The compiler infers this implements `Fn` because `x` is only read.
let add_x = |y: i32| -> i32 {
    y + x 
};

Compiler Desugaring

When you define an Fn closure, the Rust compiler generates an anonymous, unnameable struct to hold the captured environment and implements the Fn trait family for that struct. Note: Manually implementing the Fn traits on a custom struct is unstable and requires nightly-only compiler features (#![feature(unboxed_closures, fn_traits)]). The following code demonstrates the conceptual desugaring of the add_x closure above.
#![feature(unboxed_closures, fn_traits)]

// 1. Generates an anonymous struct holding an immutable reference to `x`
struct AnonymousClosure<'a> {
    x: &'a i32,
}

// 2. Implements FnOnce (Base trait defining the Output type)
impl<'a> FnOnce<(i32,)> for AnonymousClosure<'a> {
    type Output = i32;
    extern "rust-call" fn call_once(self, args: (i32,)) -> Self::Output {
        args.0 + *self.x
    }
}

// 3. Implements FnMut (Required supertrait of Fn)
impl<'a> FnMut<(i32,)> for AnonymousClosure<'a> {
    extern "rust-call" fn call_mut(&mut self, args: (i32,)) -> Self::Output {
        args.0 + *self.x
    }
}

// 4. Implements Fn
impl<'a> Fn<(i32,)> for AnonymousClosure<'a> {
    extern "rust-call" fn call(&self, args: (i32,)) -> Self::Output {
        args.0 + *self.x
    }
}

Specifying Fn in Bounds

Because every closure has a unique, unnameable type generated by the compiler, you cannot use concrete types when passing closures to functions. Instead, you must use generics with trait bounds (impl Fn) or trait objects (dyn Fn). Static Dispatch (Monomorphization):
fn execute_closure<F>(closure: F) 
where 
    F: Fn(i32) -> i32 
{
    closure(5);
    closure(10); // Can be called multiple times
}
Dynamic Dispatch (Vtable):
fn execute_boxed_closure(closure: Box<dyn Fn(i32) -> i32>) {
    closure(5);
}

Capture Inference and the move Keyword

Rust closures do not have a default capture mode. Instead, the compiler automatically infers the least restrictive capture mode required (immutable borrow, mutable borrow, or move/ownership) based on how the variables are used within the closure body. For example, a closure will capture a variable by value even without the move keyword if the body consumes the variable (e.g., by passing it to a function that takes ownership). You can force a closure to capture its environment by value regardless of usage by using the move keyword. A move closure will still implement the Fn trait, provided the closure’s body does not mutate or consume the owned variables.
let text = String::from("Hello");

// Takes ownership of `text` due to `move`, but implements `Fn` because 
// `text` is only read (not mutated or dropped) inside the body.
let print_text = move || {
    println!("{}", text);
};
In this scenario, the generated anonymous struct holds the String by value rather than by reference. However, because the call method only requires &self to read the string, the compiler successfully implements the Fn trait for the closure.
Master Rust with Deep Grasping Methodology!Learn More