Cannot move out of `mysql`, a captured variable in an `FnMut` closure

I have the following type

let mysql = web::Data::new(MySQL {
        conn: MySqlPool::connect(&conn_str).await.unwrap(),
    });

I am trying to make a cronjob using Tokio.

// Cronjob
    let mut sched = JobScheduler::new().await.unwrap();
    sched
        .add(
            Job::new_async("1/15 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    route_handlers::invoices::restaurant_invoice_creator(mysql).await;
                })
            })
            .unwrap(),
        )
        .await
        .unwrap();
    sched.start().await.unwrap();

I get the following error:

error[E0507]: cannot move out of `mysql`, a captured variable in an `FnMut` closure
    --> src/main.rs:1357:26
     |
1320 |       let mysql = web::Data::new(MySQL {
     |           ----- captured outer variable
...
1356 |               Job::new_async("1/15 * * * * *", |uuid, mut l| {
     |                                                ------------- captured by this `FnMut` closure
1357 |                   Box::pin(async move {
     |  __________________________^
1358 | |                     route_handlers::invoices::restaurant_invoice_creator(mysql).a...
     | |                                                                          -----
     | |                                                                          |
     | |                                                                          variable moved due to use in generator
     | |                                                                          move occurs because `mysql` has type `actix_web::web::Data<MySQL>`, which does not implement the `Copy` trait
1359 | |                 })
     | |_________________^ `mysql` is moved here

I am also using the mysql variable for the Actix web server:


    HttpServer::new(move || {
        let cors = Cors::default().allow_any_origin();

        App::new()
         
            .app_data(web::Data::clone(&mysql))
//...

Really annoying, how to fix?

You should be able to just clone it before moving it.

    let mysql_clone = mysql.clone();
    let mut sched = JobScheduler::new().await.unwrap();
    sched
        .add(
            Job::new_async("1/15 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    route_handlers::invoices::restaurant_invoice_creator(mysql_clone).await;
                })
            })
            .unwrap(),
        )
        .await
        .unwrap();
    sched.start().await.unwrap();

This

let mysql_clone = mysql.clone();
    let mut sched = JobScheduler::new().await.unwrap();
    sched
        .add(
            Job::new_async("1/15 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    route_handlers::invoices::restaurant_invoice_creator(mysql_clone).await;
                })
            })
            .unwrap(),
        )
        .await
        .unwrap();
    sched.start().await.unwrap();

does not work.

Error:

error[E0507]: cannot move out of `mysql_clone`, a captured variable in an `FnMut` closure
    --> src/main.rs:1353:26
     |
1348 |       let mysql_clone = mysql.clone();
     |           ----------- captured outer variable
...
1352 |               Job::new_async("1/15 * * * * *", |uuid, mut l| {
     |                                                ------------- captured by this `FnMut` closure
1353 |                   Box::pin(async move {
     |  __________________________^
1354 | |                     route_handlers::invoices::restaurant_invoice_creator(mysql_clone).await;
     | |                                                                          -----------
     | |                                                                          |
     | |                                                                          variable moved due to use in generator
     | |                                                                          move occurs because `mysql_clone` has type `actix_web::web::Data<MySQL>`, which does not implement the `Copy` trait
1355 | |                 })
     | |_________________^ `mysql_clone` is moved here

This works but is unbelievable ugly:

    let mut sched = JobScheduler::new().await.unwrap();
    sched
        .add(
            Job::new_async("1/15 * * * * *", |uuid, mut l| {
                Box::pin(async move {
                    let conn_str2: String = format!(
                        "mysql://{db_user}:{db_pass}@localhost:3306/{db_name}",
                        db_user = *config::DB_USER,
                        db_pass = *config::DB_PASS,
                        db_name = *config::DB_NAME
                    );

                    let mysql2 = MySQL {
                        conn: MySqlPool::connect(&conn_str2).await.unwrap(),
                    };

                    route_handlers::invoices::restaurant_invoice_creator(mysql2).await;
                })
            })
            .unwrap(),
        )
        .await
        .unwrap();
    sched.start().await.unwrap();

I had te redeclare mysql and conn_str. There must be a better way.

This also works. But is ridiculous:

// main fn

let conn_str: String = format!(
    "mysql://{db_user}:{db_pass}@localhost:3306/{db_name}",
    db_user = *config::DB_USER,
    db_pass = *config::DB_PASS,
    db_name = *config::DB_NAME
);

let mysql = web::Data::new(MySQL {
    conn: MySqlPool::connect(&conn_str).await.unwrap(),
});

//...

let mysql4 = mysql.clone();
let mut sched = JobScheduler::new().await.unwrap();
sched
    .add(
        Job::new_async("1/15 * * * * *", move |uuid, mut l| {
            let mysql2 = mysql.clone();
            Box::pin(async move {
                /*let conn_str2: String = format!(
                                        "mysql://{db_user}:{db_pass}@localhost:3306/{db_name}",
                                        db_user = *config::DB_USER,
                                        db_pass = *config::DB_PASS,
                                        db_name = *config::DB_NAME
                                    );

                                    let mysql2 = MySQL {
                                        conn: MySqlPool::connect(&conn_str2).await.unwrap(),
                                    };
                */
                route_handlers::invoices::restaurant_invoice_creator(mysql2).await;
            })
        })
        .unwrap(),
    )
    .await
    .unwrap();
sched.start().await.unwrap();

//...

let mysql3 = mysql4.clone();
HttpServer::new(move || {
    let cors = Cors::default().allow_any_origin();

    App::new()

        .app_data(web::Data::clone(&mysql3))
//...


You didn't provide the full code, so this is my guess:

FnMut closure is supposed to be called multiple times, but takes the ownership of mysql inside, so the closure actually can't be called multiple times.

So the solution is apparent:

  • use FnOnce as the trait bound if you don't need it to be called multiple times
  • or let the value captured by reference in FnMut closure

A similar post I answered before: Capturing Arc into async block inside closure - #4 by vague
or a simple code snippet showing the problem and solution[1]: Rust Playground


  1. the error code differs from yours though ↩ī¸Ž

1 Like

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.