Lifetime issue with warp

Hello there,

I had a function init_routes which is quite long so I'll shortened it a bit:

pub fn init_routes() {
  // POST /user
  let create_user = warp::path("user")
        .and(warp::path::end())
        .and(warp::body::json())
        .and_then(|user: User| {
            user::create_user(&user)
                .and_then(|token| Ok(warp::reply::json(&token)))
                .or_else(|e| Err(warp::reject::custom(e.compat())))
        });

let post_routes = warp::post2()
        .and(create_user).recover(customize_error);

warp::serve(post_routes ).run(([127, 0, 0, 1], 3030));
}

// omiting customize_error

// main.rs
fn main() {
  init_routes();
}

It works fine, now I'm introducing a parameter in init_routes:

pub fn init_routes(db_client: &Client) {
  // POST /user
    let create_user = warp::path("user")
        .and(warp::path::end())
        .and(warp::body::json())
        .and_then(|user: User| {
            user::create_user(&user, &db_client)
                .and_then(|token| Ok(warp::reply::json(&token)))
                .or_else(|e| Err(warp::reject::custom(e.compat())))
        });

let post_routes = warp::post2()
        .and(create_user).recover(customize_error);

warp::serve(post_routes ).run(([127, 0, 0, 1], 3030));
}

// main.rs
fn main() {
    let db_client = db::get_client(db::SERVER_ADDR);
    init_routes(&db_client);
}

I'm getting:

error[E0621]: explicit lifetime required in the type of `db_client`
   --> src\endpoints\routes.rs:225:5
    |
16  | pub fn init_routes(db_client: &Client) {
    |                               ------- help: add explicit lifetime `'static` to the type of `db_client`: `&'static redis::client::Client`
...
225 |     warp::serve(routes).run(([127, 0, 0, 1], 3030));
    |     ^^^^^^^^^^^ lifetime `'static` required

error: aborting due to previous error

If I add 'static:

error[E0597]: `db_client` does not live long enough
  --> src\main.rs:13:17
   |
13 |     init_routes(&db_client);
   |     ------------^^^^^^^^^^-
   |     |           |
   |     |           borrowed value does not live long enough
   |     argument requires that `db_client` is borrowed for `'static`
14 | }
   | - `db_client` dropped here while still borrowed


error[E0373]: closure may outlive the current function, but it borrows `db_client`, which is owned by the current function
  --> src\endpoints\routes.rs:26:19
   |
26 |         .and_then(|user: User| {
   |                   ^^^^^^^^^^^^ may outlive borrowed value `db_client`
27 |             user::create_user(&user, &db_client)
   |                                       --------- `db_client` is borrowed here
   |
note: function requires argument type to outlive `'static`
  --> src\endpoints\routes.rs:23:23
   |
23 |       let create_user = warp::path("user")
   |  _______________________^
24 | |         .and(warp::path::end())
25 | |         .and(warp::body::json())
26 | |         .and_then(|user: User| {
...  |
29 | |                 .or_else(|e| Err(warp::reject::custom(e.compat())))
30 | |         });
   | |__________^
help: to force the closure to take ownership of `db_client` (and any other referenced variables), use the `move` keyword
   |
26 |         .and_then(move |user: User| {
   |                   ^^^^^^^^^^^^^^^^^

I've fixed the second one by adding move. But still have the first one, which is true, client is not of lifetime 'static but I don't want it to be.

How can I solve this?

Whenever you get this kind of 'static lifetime requirements, the solutions is using Arc (and move closures):

use ::std::sync::Arc;

pub
fn init_routes (db_client: &Arc<Client>)
{
    let db_client = Arc::clone(db_client);
    // POST /user
    let create_user =
        warp::path("user")
            .and(warp::path::end())
            .and(warp::body::json())
            .and_then(move |user: User| {
                user::create_user(&user, &db_client)
                    .and_then(|token| Ok(warp::reply::json(&token)))
                    .or_else(|e| Err(warp::reject::custom(e.compat())))
            })
    ;
    let post_routes =
        warp::post2()
            .and(create_user)
            .recover(customize_error)
    ;

    warp::serve(post_routes)
        .run(([127, 0, 0, 1], 3030))
    ;
}

// main.rs
fn main ()
{
    let db_client = Arc::new(db::get_client(db::SERVER_ADDR));
    init_routes(&db_client);
}
2 Likes

Thanks! I'll try this later, especially having multiple routes (so multiple closures).

In this case, getting a single client for all requests to use may be problematic (i.e. there may be synchronization issues and there may be a problem if the connection shuts down and you need to reconnect. A better solution might be to use a connection pool (maybe provided by r2d2) onwed by a filter, and handling connections to the request handlers on demand.

https://github.com/kaj/fanrs/blob/master/src/server/mod.rs#L62-L126 is an example of how I do it with a r2d2 diesel pool.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.