๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

544: Lifetimes in Closures

Difficulty: 4 Level: Intermediate-Advanced Closures that capture references inherit those references' lifetimes. The `+ 'a` bound on `impl Fn` tells callers how long the returned closure is valid.

The Problem This Solves

When a closure captures a reference, that reference's lifetime becomes a constraint on the closure itself. Without expressing this constraint, you'd get dangling closures โ€” callbacks that try to access freed data:
fn make_logger(prefix: &str) -> impl Fn(&str) -> String {
 move |s| format!("{}: {}", prefix, s)
 // ERROR: cannot infer an appropriate lifetime
 // The closure captures `prefix` โ€” but how long is it valid?
}
The function doesn't say how long `prefix` lives. The caller might pass a temporary. The closure would then hold a reference to freed stack data. The `+ 'a` bound closes this gap.

The Intuition

A closure is just a struct with a `call` method. Every captured reference becomes a field. A captured `&'a str` makes the closure's struct contain a `&'a str` field โ€” so the closure itself is only valid for `'a`. The `impl Fn(&str) -> String + 'a` return type says: "I'm giving you a closure, and that closure borrows data that lives for `'a`. Keep the source data alive as long as you use the closure." `move` closures move their captures โ€” no reference, no lifetime constraint. But `move` with a reference just moves the reference, not the data, so the lifetime still applies.

How It Works in Rust

Capturing a `&str` โ€” must express the bound:
fn make_prefixer<'a>(prefix: &'a str) -> impl Fn(&str) -> String + 'a {
 //                                                              ^^^
 // + 'a: this closure borrows `prefix` which lives for 'a
 move |s| format!("{}: {}", prefix, s)
}

// Usage:
let prefix = String::from("INFO");
let log = make_prefixer(&prefix);
println!("{}", log("server started")); // fine โ€” prefix alive
drop(prefix);
// println!("{}", log("again")); // would ERROR โ€” prefix dropped, log invalid
Closure capturing multiple references:
fn make_formatter<'a>(prefix: &'a str, suffix: &'a str) -> impl Fn(&str) -> String + 'a {
 // Both captures have lifetime 'a โ€” closure valid for min(prefix, suffix)
 move |s| format!("{}{}{}", prefix, s, suffix)
}
Closure in a struct:
struct Filter<'a> {
 predicate: Box<dyn Fn(i32) -> bool + 'a>,
}

impl<'a> Filter<'a> {
 fn from_slice(allowed: &'a [i32]) -> Self {
     Filter {
         // Closure captures &'a [i32] โ€” Box must be + 'a
         predicate: Box::new(move |x| allowed.contains(&x)),
     }
 }

 fn check(&self, x: i32) -> bool { (self.predicate)(x) }
}

let allowed = vec![2, 4, 6, 8];
let filter = Filter::from_slice(&allowed);
assert!(filter.check(4));
assert!(!filter.check(3));
Closures that capture by value โ€” no lifetime issue:
fn make_sum_adder(data: &[i32]) -> impl Fn(i32) -> i32 + '_ {
 let sum: i32 = data.iter().sum(); // sum is i32 โ€” Copy
 move |x| x + sum  // captures sum (owned i32), not data โ€” but still borrows data
}
// If we only capture owned Copies, we could return impl Fn + 'static

What This Unlocks

Key Differences

ConceptOCamlRust
Closure capturesGC manages all captured values โ€” no lifetime annotationCaptured references become lifetime constraints on the closure
Returning closuresFree โ€” GC handles any captured dataMust express `+ 'a` if closure captures borrowed data
Stored callbacks`'a ref` pattern or first-class closures with GC`Box<dyn Fn + 'a>` โ€” explicit lifetime on the stored closure
Partial applicationCurrying โ€” clean, GC-managedClosure captures `&'a T` โ€” valid for `'a`. Move closures capture copies when possible
Lambda hoistingGC: closures can outlive their creation scope`'a` bound enforces: closure can't outlive its captured references
//! # 544. Lifetimes in Closures
//! Captured references constrain closure lifetimes.

/// Return a closure that captures a reference to data
/// The closure can't outlive `data` โ€” bounded by `'data`
fn make_sum_adder<'data>(data: &'data [i32]) -> impl Fn(i32) -> i32 + 'data {
    // closure captures &'data [i32]
    let sum: i32 = data.iter().sum();
    move |x| x + sum // sum is i32 (Copy), no lifetime issue
    // If we captured data directly: move |x| data.iter().sum::<i32>() + x
    // That would also require + 'data
}

/// Closure capturing a &str โ€” lifetime annotation required
fn make_prefixer<'a>(prefix: &'a str) -> impl Fn(&str) -> String + 'a {
    move |s| format!("{}: {}", prefix, s)
}

/// Closure capturing multiple references with a lifetime bound
/// 'b: 'a means prefix outlives suffix โ€” closure lives for 'a (shorter)
fn make_formatter<'a>(
    prefix: &'a str,
    suffix: &'a str,
) -> impl Fn(&str) -> String + 'a {
    move |s| format!("{}{}{}", prefix, s, suffix)
}

/// Closure in a struct โ€” must annotate
struct Filter<'a> {
    predicate: Box<dyn Fn(i32) -> bool + 'a>,
}

impl<'a> Filter<'a> {
    fn from_slice(allowed: &'a [i32]) -> Self {
        Filter {
            predicate: Box::new(move |x| allowed.contains(&x)),
        }
    }

    fn check(&self, x: i32) -> bool { (self.predicate)(x) }
}

fn main() {
    // Closure captures by-value (no lifetime issue)
    let data = vec![1, 2, 3, 4, 5];
    let add_sum = make_sum_adder(&data);
    println!("add_sum(10) = {}", add_sum(10)); // 10 + 15 = 25
    // data still usable (closure borrowed it, sum is Copy)
    println!("data: {:?}", data);

    // Closure captures &str
    let prefix = String::from("INFO");
    let log;
    {
        let p = prefix.as_str();
        log = make_prefixer(p);
        println!("{}", log("server started"));
        println!("{}", log("connected"));
    } // p is a borrow โ€” log can't be used after this if p is dropped
    // But prefix is still alive, so log is valid through prefix:
    println!("{}", make_prefixer(&prefix)("still works"));

    // Multi-lifetime closure
    let pre = String::from("[");
    let suf = String::from("]");
    let bracket = make_formatter(&pre, &suf);
    println!("{}", bracket("hello"));
    println!("{}", bracket("world"));

    // Closure in struct borrowing slice
    let allowed = vec![2, 4, 6, 8, 10];
    let filter = Filter::from_slice(&allowed);
    for n in 1..=12 {
        if filter.check(n) { print!("{} ", n); }
    }
    println!("(allowed even numbers)");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_sum_adder() {
        let data = vec![1, 2, 3];
        let f = make_sum_adder(&data);
        assert_eq!(f(0), 6);
        assert_eq!(f(10), 16);
    }

    #[test]
    fn test_prefixer() {
        let p = String::from("LOG");
        let f = make_prefixer(&p);
        assert_eq!(f("msg"), "LOG: msg");
    }

    #[test]
    fn test_filter_from_slice() {
        let allowed = vec![1, 3, 5];
        let f = Filter::from_slice(&allowed);
        assert!(f.check(1));
        assert!(f.check(3));
        assert!(!f.check(2));
    }
}
(* Closures capturing references in OCaml โ€” GC handles lifetimes *)
let make_adder_of data =
  (* data captured by GC reference *)
  let sum = List.fold_left (+) 0 data in
  fun x -> x + sum

let make_prefix_fn prefix =
  fun s -> prefix ^ ": " ^ s

let () =
  let data = [1;2;3;4;5] in
  let add_15 = make_adder_of data in
  Printf.printf "add_15(10) = %d\n" (add_15 10);

  let log = make_prefix_fn "INFO" in
  Printf.printf "%s\n" (log "something happened")