Actix server and lifetime problems

I have a tricky lifetime problem that I can't quite wrap my head around. I'm trying to start an actix server with an sql connection from outside (shared between two servers). Somehow, I can't pass the connection in due to lifetime problems. Here's a minimal example:

use actix_rt::net::TcpStream;
use actix_service::fn_service;

type SqlPool = sqlx::Pool<sqlx::sqlite::Sqlite>;

struct SomeState {
    pool: SqlPool,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let sql_pool = sqlx::sqlite::SqlitePool::connect("sqlite://users.db").await?;
    actix_server::Server::build().bind("ldap", ("0.0.0.0", 12345), move || {
        let sql_pool2 = sql_pool.clone();
        fn_service(move |_stream: TcpStream| async {
            // do something with the stream.
            let _state = SomeState {
                pool: sql_pool2.clone(),
            };
            if true {
                Err(())
            } else {
                Ok(())
            }
        })
    })?;
    Ok(())
}

AFAIU, the lambda in "bind" has static lifetime, so it should outlive whatever's inside, so it should be okay (but it isn't). The error I get:

error: lifetime may not live long enough
  --> examples/actix.rs:15:46
   |
15 |           fn_service(move |_stream: TcpStream| async {
   |  ____________________-------------------------_^
   | |                    |                       |
   | |                    |                       return type of closure `impl Future` contains a lifetime `'2`
   | |                    lifetime `'1` represents this closure's body
16 | |             // do something with the stream.
17 | |             let _state = SomeState {
18 | |                 pool: sql_pool2.clone(),
...  |
24 | |             }
25 | |         })
   | |_________^ returning this value requires that `'1` must outlive `'2`
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

I also tried to make it work with Rc, but I got the same errors. adding/removing move s in the lambda doesn't help, you get different errors of a similar nature.

The question is similar to Trouble with lifetimes in actix-web middleware - #6 by vitalyd, but as recommended there I pass everything by value/clone.

Remember that when writing move |...| async { ... }, this is a closure that returns an async block, and not a single construct. You probably need to clone it in the closure, before creating the async block, and also put move on the async block.

fn_service(move |_stream: TcpStream| {
    let pool = sql_pool2.clone();
    async move {
        // do something with the stream.
        let _state = SomeState {
            pool,
        };
        if true {
            Err(())
        } else {
            Ok(())
        }
    }
})
1 Like

Oh, wow, I had no idea you could split the async block like that! Thanks, that worked :slight_smile:

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.