'lifetime may not live long enough' while passing Arc<RwLock<T>> using Warp

I am trying to pass a DB (hashmap) as a parameter of a handler's method to make a custom Warp filters.
But, I encounter an error I can't figure out, I think it's something about Arc<RwLock>'s lifetime, but it's kinda weird since it's declared in the same scope as the handler. Here is the error:

error: lifetime may not live long enough
  --> src/main.rs:32:29
   |
32 |         .and_then(move |db| handler.method1(db))
   |                   --------- ^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                   |       |
   |                   |       return type of closure `impl Future<Output = Result<impl Reply, Rejection>>` contains a lifetime `'2`
   |                   lifetime `'1` represents this closure's body
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

error: lifetime may not live long enough
  --> src/main.rs:43:36
   |
43 |         .and_then(move |db, query| handler.method2(db, query))
   |                   ---------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                   |              |
   |                   |              return type of closure `impl Future<Output = Result<impl Reply, Rejection>>` contains a lifetime `'2`
   |                   lifetime `'1` represents this closure's body
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

I wrote this sample from my actual code if you want to reproduce the error:

use serde::Deserialize;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;

use warp::Filter;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Arc::new(RwLock::new(HashMap::new()));
    let handler = Handler::new().await?;

    let routes = filter1(db.clone(), handler.clone()).or(filter2(db, handler));

    warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
    Ok(())
}

pub type Db = Arc<RwLock<HashMap<String, (String, String)>>>;

pub fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || db.clone())
}

pub fn filter1(
    db: Db,
    handler: Handler,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path("1")
        .and(warp::get())
        .and(with_db(db.clone()))
        .and_then(move |db| handler.method1(db))
}

pub fn filter2(
    db: Db,
    handler: Handler,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path("2")
        .and(warp::get())
        .and(with_db(db.clone()))
        .and(warp::query::query::<Query>())
        .and_then(move |db, query| handler.method2(db, query))
}

#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct Query {
    pub field1: String,
    pub field2: u8,
    //...
}

#[derive(Clone)]
pub struct Handler {
    // ...
}

impl Handler {
    pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
        // ...
        Ok(Self {
            // ...
        })
    }

    pub async fn method1(&self, db: Db) -> Result<impl warp::Reply, warp::Rejection> {
        /*
        SOME CODE
         */
        db.write().await.insert(
            "Name1".to_string(),
            ("Val1".to_string(), "Val2".to_string()),
        );
        /*
        SOME CODE
         */
        let dummy_response = vec![1, 3, 7, 13];
        Ok(warp::reply::with_status(
            warp::reply::json(&dummy_response),
            warp::http::StatusCode::OK,
        ))
    }

    /// Once the user has been redirected to the callback URL, we get access to their authorization code.
    pub async fn method2(&self, db: Db, query: Query) -> Result<impl warp::Reply, warp::Rejection> {
        /*
        SOME CODE
         */
        let val = db.write().await.remove("Name1");
        /*
        SOME CODE
         */
        let dummy_response = vec![1, 3, 7, 13];
        Ok(warp::reply::with_status(
            warp::reply::json(&dummy_response),
            warp::http::StatusCode::OK,
        ))
    }
}

Thank you in advance for your help!

The error message is though; the problem is however easy to understand even without looking at the error: This closure

move |db| handler.method1(db)

captures the handler: Handler and returns the future from the call handler.method1(db). However, method1 is an async fn on &self, which means the future it returns captures all the arguments including the &Handler borrowed from the closure. Captured variables from a closure can however not be borrowed by values returned from the closure, so there’s your problem.

Now onto the solution: The returned Future must have an owned handle to Handler. As far as I understand your code (based on the fact that you clone one in line 13), cloning Handlers is okay for this purpose (in case it isn’t, you can alternatively wrap it in an Arc and then clone that Arc<Handler> instead). Then you can create a clone of the Handler in the closure and create a future that owns it using async/await notation. The final code that works looks like this:

pub fn filter1(
    db: Db,
    handler: Handler,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path("1")
        .and(warp::get())
        .and(with_db(db.clone()))
        .and_then(move |db| {
            let handler = handler.clone();
            async move { handler.method1(db).await }
        })
}

pub fn filter2(
    db: Db,
    handler: Handler,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path("2")
        .and(warp::get())
        .and(with_db(db.clone()))
        .and(warp::query::query::<Query>())
        .and_then(move |db, query| {
            let handler = handler.clone();
            async move { handler.method2(db, query).await }
        })
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.