41. Async Closures — Pass Async Code Like Any Other Closure
Accepting an async callback used to mean a tangle of Fn(T) -> Fut where Fut: Future. Rust 1.85 stabilizes async closures — write async |x| { ... } and accept them with impl async Fn(T) -> U.
The old workaround
Before Rust 1.85, accepting an async function as a parameter meant spelling out the future type explicitly:
| |
It compiles, but the signature is noisy. It gets worse once you need FnMut, higher-ranked lifetimes, or closures that borrow from their captures.
The new way
| |
async |...| { ... } is the syntax for an async closure. Use AsyncFn(T) -> U bounds at call sites — AsyncFn, AsyncFnMut, and AsyncFnOnce mirror the regular Fn family and were stabilized alongside async closures in Rust 1.85.
Capturing state works naturally
Async closures capture their environment exactly like regular closures:
| |
No extra boxing or lifetime gymnastics — the closure borrows base just as a sync closure would.
Apply it twice
| |
Why it matters
The real payoff is in generic async APIs — retry helpers, middleware, event hooks — anywhere you’d pass a callback. Instead of Pin<Box<dyn Future>> boilerplate you get a clean bound:
| |
Async closures are available in Rust 1.85+ (stable since February 2025). Make sure your crate uses edition = "2021" or later.