Search code examples
rustasync-awaitrust-tokio

Using same value in multiple "new_repeated_async" jobs in tokio_cron_scheduler


Here is a simplified version of the problem the problem that I'm having:

use std::time::Duration;
use tokio_cron_scheduler::{JobScheduler, Job};

#[derive(Clone, Debug)]
struct Obj {
    a: String,
    b: u64,
}

#[tokio::main]
async fn main() {

    let obj = Obj {
        a: "A".to_string(),
        b: 64,
    };

    let sched = JobScheduler::new().await.unwrap();

    let job = Job::new_repeated_async(Duration::from_secs(2), |_, _| Box::pin(async move {
        foo(&obj);
    })).unwrap();
    sched.add(job).await.unwrap();

    let job = Job::new_repeated_async(Duration::from_secs(5), |_, _| Box::pin(async move {
        bar(&obj);
    })).unwrap();
    sched.add(job).await.unwrap();

    sched.start().await.unwrap();

    tokio::time::sleep(core::time::Duration::from_secs(100)).await;
}


fn foo(o: &Obj) {
    println!("{}", o.a);
}

fn bar(o: &Obj) {
    println!("{}", o.b);
}

But this doesn't work because I get the error "cannot move out of obj, a captured variable in an FnMut closure.

My initial thought was that I just had to wrap "obj" in an Arc and clone it before creating the job:

let obj = Arc::new(Obj {
    a: "A".to_string(),
    b: 64,
});
...
let o = obj.clone();
let job = Job::new_repeated_async(Duration::from_secs(2), |_, _| Box::pin(async move {
    foo(&o);
})).unwrap();
...

but this doesn't work either and I get the same error.

I then just tried to clone obj without the Arc before passing it to the job, and again the same error.

I found similar error messages on the site but none of them seemed to be able to help me solve this one. Can anyone help?


Solution

  • You didn't clone in the right places. You need to clone for each async move future returned, and for the first closure too:

    let job = Job::new_repeated_async(Duration::from_secs(2), {
        let obj = Arc::clone(&obj);
        move |_, _| {
            let obj = Arc::clone(&obj);
            Box::pin(async move {
                foo(&obj);
            })
        }
    })
    .unwrap();
    sched.add(job).await.unwrap();
    
    let job = Job::new_repeated_async(Duration::from_secs(5), move |_, _| {
        let obj = Arc::clone(&obj);
        Box::pin(async move {
            bar(&obj);
        })
    })
    .unwrap();