Please help me to understand lifetime

I'll assume you're currently using the solution from your last question. The problem is that every callback is required to return a Future + 'static, but any async function that captures an &'a mut Context can only return a Future + 'a (since the future must store the reference). This is what results in the compiler error: it's saying that the opaque future type corresponding to index is not 'static.

Unfortunately, the fix for this is somewhat involved. We want to assert that the callback returns a Future + 'a when given an &'a mut Context, for any lifetime 'a. This can be done with a higher-rank trait bound. But since the future type is distinct from the callback type, we must add a helper Callback<'a> trait, which asserts that a function has type Fn(&'a mut Context) -> impl Future + 'a. Altogether, it looks something like this (Rust Playground):

use futures::{future::BoxFuture, Future};
use std::{marker::Send, sync::Arc};

struct Context {
    body: String,
}

trait Callback<'a> {
    fn call(&self, ctx: &'a mut Context) -> BoxFuture<'a, ()>;
}

impl<'a, T, F> Callback<'a> for F
where
    F: Fn(&'a mut Context) -> T,
    T: Future<Output = ()> + Send + 'a,
{
    fn call(&self, ctx: &'a mut Context) -> BoxFuture<'a, ()> {
        Box::pin(self(ctx))
    }
}

struct Store {
    gets: Vec<(String, Arc<Box<dyn for<'a> Callback<'a>>>)>,
}

impl Store {
    fn get<F>(&mut self, path: &str, callback: F)
    where
        F: for<'a> Callback<'a> + 'static,
    {
        self.gets
            .push((path.to_string(), Arc::new(Box::new(callback))));
    }
}

async fn index(ctx: &mut Context) {
    ctx.body = "Hello World".to_string();
    println!("Hello");
}

fn main() {
    let mut s = Store { gets: Vec::new() };
    s.get("/", index);
}
2 Likes