Please help me to understand lifetime

Code

fn get<T, F>(&mut self, path: &str, callback: F)
    where
        F: Fn(&mut Context) -> T + 'static + Send + Sync,
        T: Future<Output = ()> + 'static + Send + Sync,
    {
        self.gets.push((
            path.to_string(),
            Arc::new(Box::new(move |c| Box::pin(callback(c)))),
        ));
    }

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

get("/", index);

Error

mismatched types
expected associated type `<for<'_> fn(&mut structs::Context) -> impl futures::Future<Output = ()> {index} as FnOnce<(&mut structs::Context,)>>::Output`
   found associated type `<for<'_> fn(&mut structs::Context) -> impl futures::Future<Output = ()> {index} as FnOnce<(&mut structs::Context,)>>::Output`
the required lifetime does not necessarily outlive the static lifetimerustcE0308
get.rs(101, 32): the lifetime requirement is introduced here

The issue is I am using mutable reference Fn(&mut Context) & rust unable to get the lifetime.
Thank you

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