Function Pointers, Closures

Here's an example of a closure that implements FnOnce by capturing and consuming (moving) a variable from its environment:

fn main() {
    let name = String::from("Alice"); // A String we'll move into the closure
    
    // This closure implements FnOnce because it moves 'name'
    let greet = || {
        println!("Hello, {}!", name);
        name // This moves 'name' out of the closure
    };
    
    // We can only call this once because it consumes 'name'
    let moved_name = greet();
    println!("Moved name: {}", moved_name);
    
    // greet(); // This would fail - can't call FnOnce twice!
    
    // Demonstrate passing to a function that expects FnOnce
    run_once(greet); // Note: we can't actually do this because greet was already consumed
}

fn run_once<F: FnOnce() -> String>(f: F) {
    let result = f();
    println!("From run_once: {}", result);
}

Key points about FnOnce:

  1. Captures and consumes environment variables (moves them)
  2. Can only be called once because it consumes what it captures
  3. Most restrictive closure trait (all closures implement FnOnce)

A working version that demonstrates passing to run_once:

fn main() {
    let name = String::from("Bob");
    
    // This closure moves 'name'
    let greet = move || {
        println!("Hello, {}!", name);
        name
    };
    
    // We can pass it to a function expecting FnOnce
    run_once(greet);
    
    // Can't use 'greet' or 'name' after this point
}

fn run_once<F: FnOnce() -> String>(f: F) {
    let result = f();
    println!("From run_once: {}", result);
}

Why this is FnOnce:

  • The closure uses move to take ownership of name
  • It returns name, moving it out of the closure
  • After the first call, the closure can't be called again because its captured value was moved

Relationship between the traits:

  • All closures implement FnOnce
  • Closures that don't move captured values implement FnMut
  • Closures that don't mutate captured values implement Fn

So: Fn is a subset of FnMut which is a subset of FnOnce

In Rust, closure parameters don't require explicit type annotations in most cases because the compiler can infer their types from the context. This is different from regular fn functions which always require type annotations.

Closure Type Inference Example:

fn main() {
    // Type inferred from usage
    let add = |a, b| a + b;
    println!("{}", add(3, 5)); // Compiler knows a and b are i32
    
    // Type inferred from variable annotation
    let add: fn(i32, i32) -> i32 = |a, b| a + b;
}

When Types Are Needed:

  1. Ambiguous Cases (when the compiler can't infer):
#![allow(unused)]
fn main() {
let ambiguous = |x| x; // Error: cannot infer type
ambiguous(5); // Adding this would fix it
}
  1. Explicitly Wanted (for clarity):
#![allow(unused)]
fn main() {
let add = |a: i32, b: i32| -> i32 { a + b };
}

Your Specific Example:

#![allow(unused)]
fn main() {
let mut increment_and_add = |a, b| { 
    // a and b types inferred from first usage
    // If you later call: increment_and_add(1u8, 2u8),
    // then a and b would be u8
};
}

Key Differences from Regular Functions:

| Feature | Closure (||) | Regular Function (fn) | |---------------|---------------|----------------------| | Type annotations | Optional | Required | | Capturing env | Yes | No | | Traits | Fn, FnMut, FnOnce | N/A | | As parameters | Usually generic with trait bounds | Directly by type |

Best Practice:

  • Omit types when they're obvious from context
  • Add types when:
    • The closure is complex
    • You want to document expected types
    • The compiler can't infer them