๐Ÿฆ€ Functional Rust
๐ŸŽฌ Closures in Rust Fn/FnMut/FnOnce, capturing environment, move closures, higher-order functions.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Closures capture variables from their environment โ€” by reference, mutable reference, or by value (move)

โ€ข Three traits: Fn (shared borrow), FnMut (mutable borrow), FnOnce (takes ownership)

โ€ข Higher-order functions like .map(), .filter(), .fold() accept closures as arguments

โ€ข move closures take ownership of captured variables โ€” essential for threading

โ€ข Closures enable functional patterns: partial application, composition, and strategy

529: Async Closures

Difficulty: 4 Level: Advanced Closures that return `Future`s โ€” the standard pattern for async callbacks, retry logic, and concurrent pipelines.

The Problem This Solves

You want to pass an async operation as a callback โ€” `retry(3, || fetch(url))` where each attempt is async. But `fetch(url)` returns a `Future`, and closures returning `Future`s have complex type requirements. The first attempt usually produces compiler errors about `Fn` bounds not being satisfied or lifetimes being wrong. True `async ||` closures are nightly-only in Rust. On stable, you write `|| async { ... }` โ€” a regular closure that returns an anonymous `async` block. The types get intricate: you need `Fn() -> impl Future<Output = T>` or, for dynamic dispatch, `Box<dyn Fn() -> BoxFuture<'a, T>>`. Without understanding this pattern, async callbacks become a wall of incomprehensible errors. With it, you can build retry logic, concurrent processing, and async event handlers cleanly.

The Intuition

An async closure is a closure that returns a future instead of a value. Calling it starts a computation; awaiting the result runs it. Think of it like a factory that produces `Promise`s in JavaScript: `() => fetch(url)` โ€” each call creates a new pending promise. In JavaScript: `const withRetry = (fn, n) => { for(let i=0;i<n;i++) try { return await fn() } catch {} }` โ€” `fn` is an async callback. Rust's equivalent requires spelling out the `Future` type explicitly, because Rust's type system doesn't have implicit promise boxing. The workhorse pattern: `|x| async move { use_x(x).await }`. The `move` transfers `x` into the async block, making the resulting `Future` `'static` (owns its data, not borrowing from the closure's scope).

How It Works in Rust

use std::future::Future;
use std::pin::Pin;

// Type alias for boxed futures โ€” common in async callback APIs
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

// Retry pattern: async closure called up to N times
async fn retry<T, E, F, Fut>(max_attempts: usize, f: F) -> Result<T, E>
where
 F: Fn() -> Fut,                // closure takes no args, returns a Future
 Fut: Future<Output = Result<T, E>>,
{
 for attempt in 0..max_attempts {
     match f().await {           // create and await a new future each call
         Ok(val) => return Ok(val),
         Err(e)  => {
             eprintln!("Attempt {} failed", attempt + 1);
             if attempt + 1 == max_attempts { return Err(e); }
         }
     }
 }
 unreachable!()
}

// Pattern 1: closure returning async block (stable Rust)
let async_double = |x: i32| async move { x * 2 };
// Type: impl Fn(i32) -> impl Future<Output = i32>

// Pattern 2: async map โ€” process items sequentially
async fn async_map<T, U, F, Fut>(items: Vec<T>, f: F) -> Vec<U>
where F: Fn(T) -> Fut, Fut: Future<Output = U> {
 let mut results = Vec::new();
 for item in items {
     results.push(f(item).await);   // sequential: one at a time
 }
 results
}

// Pattern 3: boxed async closure for dynamic dispatch
fn make_processor(prefix: String) -> impl Fn(i32) -> BoxFuture<'static, String> {
 move |x: i32| {
     let prefix = prefix.clone();     // clone for each future (owned)
     Box::pin(async move {            // Box::pin for heap-allocated Future
         format!("{}: {}", prefix, x * 2)
     })
 }
}

// Pattern 4: async with captured reference โ€” use move to own it
async fn process_all(urls: Vec<String>) -> Vec<Result<String, String>> {
 let mut results = Vec::new();
 for url in urls {
     // move url into the async block โ€” Future owns it
     let result = (|| async move { fake_fetch(&url).await })().await;
     results.push(result);
 }
 results
}
The key rule: if the closure's `Future` must be `'static` (sent to a thread or stored), use `async move { ... }` to transfer ownership into the future. If the future only lives within the closure's scope, you can borrow.

What This Unlocks

Key Differences

ConceptOCamlRust
Async functionExternal libraries (Lwt, Async)`async fn` / `impl Future` โ€” built-in
Async closure`fun x -> Lwt.return x``\x\async move { x }` (stable) or `async \x\{ x }` (nightly)
Await`Lwt.bind` / `let*``.await` โ€” postfix operator
Boxed future`Lwt.t` (always boxed)`Pin<Box<dyn Future<...>>>` โ€” explicit
`'static` futureGC handles`async move` โ€” closure owns all captures
//! # 529. Async Closures
//! Patterns for async callbacks using closures that return Futures.
//! Note: True `async |x| {...}` is nightly-only; we use `|x| async { ... }`.

use std::future::Future;
use std::pin::Pin;

/// Type alias for a boxed future (common in async callback patterns)
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

/// Accept an async callback: closure returns a Future
fn for_each_async<T, F, Fut>(items: Vec<T>, f: F)
where
    F: Fn(T) -> Fut,
    Fut: Future<Output = ()>,
{
    // In real async code, you'd use an executor here
    // For demonstration, we show the type pattern
    let _futures: Vec<Fut> = items.into_iter().map(f).collect();
    println!("[for_each_async: would await all futures in real runtime]");
}

/// Async map: transform items with an async function
async fn async_map<T, U, F, Fut>(items: Vec<T>, f: F) -> Vec<U>
where
    F: Fn(T) -> Fut,
    Fut: Future<Output = U>,
{
    let mut results = Vec::new();
    for item in items {
        results.push(f(item).await);
    }
    results
}

/// Retry pattern with async closure
async fn retry<T, E, F, Fut>(max_attempts: usize, f: F) -> Result<T, E>
where
    F: Fn() -> Fut,
    Fut: Future<Output = Result<T, E>>,
{
    let mut last_err = None;
    for attempt in 0..max_attempts {
        match f().await {
            Ok(val) => return Ok(val),
            Err(e) => {
                println!("Attempt {} failed, retrying...", attempt + 1);
                last_err = Some(e);
            }
        }
    }
    Err(last_err.unwrap())
}

/// Simulated async fetch (returns immediately in this demo)
async fn fake_fetch(url: &str) -> Result<String, String> {
    // In real code: HTTP request
    if url.contains("fail") {
        Err(format!("Failed to fetch {}", url))
    } else {
        Ok(format!("Data from {}", url))
    }
}

/// Async closure pattern: |x| async move { ... }
fn make_async_processor(prefix: String) -> impl Fn(i32) -> BoxFuture<'static, String> {
    move |x: i32| {
        let prefix = prefix.clone();
        Box::pin(async move {
            // Simulated async work
            format!("{}: {}", prefix, x * 2)
        })
    }
}

fn main() {
    // Create a minimal async runtime for demo
    let rt = std::sync::Arc::new(std::sync::Mutex::new(()));

    // Show async closure types
    println!("=== Async closure patterns ===");

    // Pattern 1: closure returning async block
    let async_double = |x: i32| async move { x * 2 };
    println!("async_double type created (returns Future<Output=i32>)");

    // Pattern 2: closure returning BoxFuture (for dynamic dispatch)
    let async_fmt: Box<dyn Fn(i32) -> BoxFuture<'static, String>> =
        Box::new(|x| Box::pin(async move { format!("value={}", x) }));
    println!("dynamic async closure created");

    // Pattern 3: async map via block_on simulation
    // (In real code: tokio::main or async_std::main)
    println!("\n=== Simulating async pipeline ===");

    // Show the patterns without actually running a runtime
    let processor = make_async_processor("result".to_string());
    println!("Processor created โ€” would process: [1, 2, 3, 4, 5]");

    // Demonstrate async retry pattern (conceptual)
    println!("\nRetry pattern: would retry up to 3 times on failure");
    println!("Pattern: retry(3, || async {{ fetch(url).await }})");

    // for_each_async demonstration
    for_each_async(vec![1, 2, 3], |x| async move {
        println!("Processing: {}", x);
    });

    // Drop suppression
    let _ = async_double;
    let _ = async_fmt;
    let _ = rt;
}

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

    #[test]
    fn test_async_closure_type() {
        // Verify the closure compiles and returns a Future
        let f = |x: i32| async move { x + 1 };
        let future = f(41);
        // Can't easily run without a runtime in tests without tokio
        // Just verify it compiles and is a valid Future type
        let _ = future;
    }

    #[test]
    fn test_make_async_processor() {
        let proc = make_async_processor("test".to_string());
        let _future = proc(5); // returns Future<Output=String>
        // Just verify it compiles
    }

    // With tokio as dev-dependency, you could write:
    // #[tokio::test]
    // async fn test_async_map() {
    //     let results = async_map(vec![1,2,3], |x| async move { x * 2 }).await;
    //     assert_eq!(results, vec![2,4,6]);
    // }
}
(* Async closures in OCaml โ€” using Lwt library concept *)
(* Standard OCaml doesn't have built-in async, this shows the pattern *)

(* Simulating async with continuation-passing style *)
type 'a promise = 'a  (* simplified *)

let async_compute f x = f x  (* would be Lwt.return (f x) in real Lwt *)
let async_map f promise = f promise

let () =
  (* Callback-style "async" *)
  let fetch url callback =
    (* simulate: in real code this would be async *)
    let result = Printf.sprintf "Data from %s" url in
    callback result
  in

  fetch "https://api.example.com/data" (fun data ->
    Printf.printf "Received: %s\n" data;
    fetch "https://api.example.com/more" (fun more ->
      Printf.printf "Also received: %s\n" more
    )
  )