I need help clearing up confusion with async-related lifetimes
⚓ Rust 📅 2025-12-30 👤 surdeus 👁️ 4Hello fellow Rustaceans,
When compiling the following code:
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_imports)]
use futures::Future;
use tokio::sync::mpsc::Receiver;
type Event = ();
trait EventHandler<'a>: FnMut(&'a Event) -> Self::Fut {
type Fut: Future<Output = ()>;
}
impl<'a, F, Fut> EventHandler<'a> for F
where
F: FnMut(&'a Event) -> Fut,
Fut: Future<Output = ()> + 'a,
{
type Fut = Fut;
}
async fn process_event<'a>(
mut events: Receiver<Event>,
handlers: &mut [impl EventHandler<'a>],
) -> Option<()> {
match events.recv().await {
Some(event) => {
for event_handler in handlers {
event_handler(&event).await;
}
Some(())
}
None => None,
}
}
I get the following error:
error[E0597]: `event` does not live long enough
--> src/lib.rs:28:31
|
21 | async fn process_event<'a>(
| -- lifetime `'a` defined here
...
26 | Some(event) => {
| ----- binding `event` declared here
27 | for event_handler in handlers {
28 | event_handler(&event).await;
| --------------^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `event` is borrowed for `'a`
...
31 | }
| - `event` dropped here while still borrowed
|
note: requirement that the value outlives `'a` introduced here
--> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:163:37
|
163 | pub const trait FnMut<Args: Tuple>: FnOnce<Args> {
| ^^^^^^^^^^^^
From what I understand about the sugar-coating of async/await, the event_handler(&event).await somewhat create a Future (the event_handler() part), then polls it (the .await part), and returns immediately if the poll returns Pending.
I'm unsure whether the magic that translates the async function to a state-machine takes care of internal lifetimes, but it looks like it doesn't.
From what I understand, the error here is that event_handler() returns a Future that (correctly, I believe) has 'a as a lifetime. And it looks like the compiler assumes the .await will get out of the scope of the Some(event) pattern, hence the complaint about event not living long-enough.
But reading this code literally, and ignoring the internals of the generated state-machine, the future never moves outside of the scope of the Some(event) pattern, thus the future should never outlive event.
What's wrong in my reasoning?
PS: This is not a XY problem because I want to understand what's technically wrong here, although I'm also interested in what should be the right pattern when trying to call back to a sequence of generic handlers (it looks like the mutable slice of FnMut(&Event) -> Future<Output = ()> won't cut it).
4 posts - 3 participants
🏷️ Rust_feed