Is there another option to share an Arc in multiple closures besides cloning it before each closure?

I have something like this:

use std::sync::Arc;

fn main() {
    let arc = Arc::new(42);
    move || { arc.clone() };
    move || { arc.clone() };
}

I am getting:

error[E0382]: capture of moved value: `arc`
 --> src/main.rs:6:19
  |
5 |         move || { arc.clone() };
  |         ------- value moved (into closure) here
6 |         move || { arc.clone() };
  |                   ^^^ value captured here after move
  |
  = note: move occurs because `arc` has type `std::sync::Arc<i32>`, which does not implement the `Copy` trait

I understand why I am getting this: the clone isn't called before arc is passed to the closure. I can fix this by defining each closure in a function and clone the Arc before passing it to the closure, but is there another option?


Solution 1:

There is no way around it. You should clone the Arc before it is used in a closure. The common pattern is to re-bind the cloned Arc to the same name in a nested scope:

use std::sync::Arc;

fn main() {    
    let arc = Arc::new(42);
    {
        let arc = arc.clone();
        move || { /* do something with arc */ };
    }
    {
        let arc = arc.clone();
        move || { /* do something else with arc */ };
    }
}

This is usually done together with thread::spawn():

use std::sync::{Arc, Mutex};
use std::thread;

const NUM_THREADS: usize = 4;

fn main() {
    let arc = Arc::new(Mutex::new(42));
    for _ in 0..NUM_THREADS {
        let arc = arc.clone();
        thread::spawn(move || {
            let mut shared_data = arc.lock().unwrap();
            *shared_data += 1;
        });
    }
}

Solution 2:

is there another option?

Because this pattern of cloning things before defining a closure is somewhat common, some people have proposed adding something like clone || as an analog to move ||. I wouldn't hold out hope for this happening, but a number of comments there point out that macros can solve the case fairly well.

Several crates provide some form of this macro:

  • closet
  • capture
  • clone_all

It's likely that many projects define their own macro to do something similar. For example, the WASM example rust-todomvc defines:

macro_rules! enclose {
    ( ($( $x:ident ),*) $y:expr ) => {
        {
            $(let $x = $x.clone();)*
            $y
        }
    };
}

Which can be used as:

fn main() {
    let arc = Arc::new(42);
    enclose! { (arc) move || arc };
    enclose! { (arc) move || arc };
}