error: lifetime may not live long enough -- returning this value requires that `'1` must outlive `'2`
ID: rust/e0700-async-closure-lifetime
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 1 | active | — | — | — |
Root Cause
Async closures capture references that may not outlive the future they produce. The solution depends on context: usually adding 'move' to take ownership, or using named lifetimes on the function signature.
genericWorkarounds
-
85% success Add 'move' to async closures to take ownership of captured variables
Change 'tokio::spawn(async { ... })' to 'tokio::spawn(async move { ... })'. The 'move' keyword transfers ownership of all captured variables into the closure, satisfying the 'static bound that spawn requires. -
80% success Use named lifetimes on the function signature
Instead of 'async fn foo(s: &str) -> &str', use 'async fn foo<'a>(s: &'a str) -> &'a str'. Named lifetimes show the compiler the relationship between input and output references.
Sources: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html
-
78% success Use Pin<Box<dyn Future + Send + 'static>> for trait-object futures
When returning futures from trait methods or storing them in collections, the compiler cannot infer lifetimes. Boxing with explicit bounds resolves it: fn foo() -> Pin<Box<dyn Future<Output = T> + Send + 'static>>.
Dead Ends
Common approaches that don't work:
-
Adding 'static lifetime bound to everything
70% fail
'static means the value lives for the entire program. This is overly restrictive: it prevents borrowing local data, forces all data to be owned or leaked, and leads to excessive .clone() calls. Most async code does not need 'static — it needs clear ownership.
-
Using Box::leak to make references 'static
90% fail
Box::leak deliberately leaks memory. The value is never freed. This 'works' at the cost of a memory leak every time the code runs. It is a code smell, not a solution.
-
Wrapping everything in Arc<Mutex<T>>
60% fail
Arc<Mutex<T>> adds unnecessary synchronization overhead and complexity. Often the issue is simply that the closure needs 'move' to take ownership, or the function needs a named lifetime — not interior mutability.