Search code examples
rustclosureslifetimeborrow-checkertrait-objects

How to pass an Arc clone to a closure?


I'm trying to write a closure that uses an Arc by cloning it. Ideally I'd like to have the clone inside the closure, but I'm kinda forced to pass the original Arc, which might be the reason I'm getting the error:

use std::sync::Arc;
use std::sync::Condvar;
use std::sync::Mutex;
use std::collections::VecDeque;

type Fifo<T> = Arc<(Mutex<VecDeque<T>>, Condvar)>;

fn executor(f: Box<dyn Fn()>) {
    f();
}

fn main() {
    let a = Fifo::<u8>::new(
        (Mutex::new(VecDeque::new()), Condvar::new())
    );
    
    let r = Box::new(||{
        let f = a.clone();
        f.0.lock().unwrap().push_back(0);
    });
    executor(r);
}

Error:

error[E0597]: `a` does not live long enough
  --> src/main.rs:19:17
   |
18 |     let r = Box::new(||{
   |                      -- value captured here
19 |         let f = a.clone();
   |                 ^ borrowed value does not live long enough
...
22 |     executor(r);
   |              - cast requires that `a` is borrowed for `'static`
23 | }
   | - `a` dropped here while still borrowed

error: aborting due to previous error

I thought changing to

let r = Box::new(||{
    //let f = a.clone();
    a.0.lock().unwrap().push_back(0);
});

would force the closure to decide to clone a, therefore fixing the problem, but I get the same error.

How can I pass an Arc to a closure?


Solution

  • Clone the Arc outside the closure and then move the clone into the closure. Example:

    use std::collections::VecDeque;
    use std::sync::Arc;
    use std::sync::Condvar;
    use std::sync::Mutex;
    
    type Fifo<T> = Arc<(Mutex<VecDeque<T>>, Condvar)>;
    
    fn executor(f: Box<dyn Fn()>) {
        f();
    }
    
    fn main() {
        let a = Fifo::<u8>::new((Mutex::new(VecDeque::new()), Condvar::new()));
    
        let f = a.clone();
        let r = Box::new(move || {
            f.0.lock().unwrap().push_back(0);
        });
    
        executor(r);
    }
    

    playground