Cannot move out of captured variable in an `Fn` closure


#1

Hello I’m using actix-web to create a minimalist rest web app that gets data from sqlite database. I would like to share the closure that creates connection to the database to each handler via application state struct. I know I can solve my problem by sharing db_filepath and calling get_db_connection in the handlers. But my approach is more elegant and I’m interested in why my approach does not work.

Here is a minimal example with errors:

fn get_db_connection(db_filepath: &String) -> rusqlite::Connection {
    let conn = rusqlite::Connection::open(db_filepath).unwrap();
    conn
}

struct AppState {
    get_db: Box<dyn FnMut() -> rusqlite::Connection>,
}

fn main() {
	let db_filepath = "some/path.db".to_string() // is read from config file otherwise

	let f = Box::new(move || { get_db_connection(&db_filepath) }); // f is captured outer variable

	actix_web::server::new(move || {
        actix_web::App::with_state(AppState { get_db: f }) // cannot move out of captured variable in an `Fn` closure
            .scope("call-records", |call_record| {
                call_record
                    .resource("/", |r| {
                        r.get().with(list_call_records);
                    })
            })
        }).bind("127.0.0.1:8088")
            .unwrap()
            .run();

}

fn list_call_records(req: HttpRequest<AppState>) -> Result<Json<Vec<CallRecord>>> {
    let conn = (&req.state().get_db)();
    // ...
    // ...
    Ok(Json(records))
}

#2

Note that you have two layers of move || closures.

actix_web::server::new wants to call its closure any number of times, each time creating a new inner closure that owns its data, so it needs ability to have infinite number of copies of f.

But there’s only one f, with only one copy of db_filepath, where you need infinite number of copies of db_filepath.

You can move f or db_filepath first to the closure of actix_web::server::new, and then clone it every time you create a new app.

Generally with Actix you’ll need to wrap lots of things in Arc, because each thread gets its own server with its own copy of the data. Wrapping entire AppState in Arc is also usually a good idea (assuming you want it shared and the same for all threads).


#3

Thank you for the clarification.

I have changed my code to:


fn main() {
    let db_filepath = "some/path.db".to_string() // is read from config file otherwise

    actix_web::server::new(move || {
        let s = db_filepath.clone(); // this looks weird but Box::new(move || { get_db_connection(&db_filepath.clone()) }) does not work
        let f = Box::new(move || { get_db_connection(&s) });
        actix_web::App::with_state(AppState { get_db: f }) 
         // ...

}

fn list_call_records(req: HttpRequest<AppState>) -> Result<Json<Vec<CallRecord>>> {
    let conn = (&req.state().get_db)(); // cannot borrow data in a `&` reference as mutable
    // ...
    // ...
    Ok(Json(records))
}

Now I have problem in the handler but I’m not doing anything with mutable?


#4

In GC languages there’s no difference between expression assigned to a variable or not, and variables are just a cosmetic help. In Rust variables have semantic meaning - they extend lifetimes.

So Box::new(move || { get_db_connection(db_filepath.clone()) }); still needs a copy of db_filepath to live in each closure to be cloned from.

OTOH let s = db_filepath.clone() means there’s 1 copy of db_filepath outside, and each inner closure has 1 copy of s for itself (and doesn’t touch db_filepath any more).


#5

I’m not sure about your second problem. Perhaps get_db wants &mut self or is FnMut?

If you have a shared reference and need to make it mutable, Mutex is can do it.


#6

I’ve solved the last error too. The problem was

struct AppState {
    get_db: Box<dyn FnMut() -> rusqlite::Connection>,
}

type of the Box should be without mut dyn Fn() -> rusqlite::Connection

struct AppState {
    get_db: Box<dyn Fn() -> rusqlite::Connection>,
}