How to avoid explicitly cloning a variable that is moved to a closure?

Is there a way to avoid creating temporary variables a variable is captured in multiple closures:

  • The move keyword in the closures is required
  • It's fine to use any other container instead of Rc as long as the inner value is passed to the closures
  • It's fine to clone the container, I tried the Copy trait but does not work as the inner value is a struct
use std::rc::Rc;
use std::cell::RefCell;

#[derive(Debug)]
struct MyStruct { value: u32 }

fn main() {
    
    let shared = Rc::new(RefCell::new(MyStruct { value: 42}));
    let shared_clone_1 = shared.clone();    // clone 1
    let shared_clone_2 = shared.clone();   // clone 2
    let shared_clone_3 = shared.clone();   // clone 3
    let shared_clone_4 = shared.clone();   // clone 4: can these clones be hidden?

    let closure_1 = move || { println!("closure_1 {:?}", shared); };
    let closure_2 = move || { println!("closure_2 {:?}", shared_clone_1); };
    let closure_3 = move || { println!("closure_3 {:?}", shared_clone_2); };
    let closure_4 = move || { println!("closure_4 {:?}", shared_clone_3); };
    let closure_5 = move || { println!("closure_5 {:?}", shared_clone_4); };
    
    closure_1();
    closure_2();
    closure_3();
    closure_4();
    closure_5();
}

For the code given, you can capture a reference instead.

Some relevant lang-dev document. Ergonomic ref-counting - Rust Project Goals
Simplify lightweight clones, including into closures and async blocks by joshtriplett · Pull Request #3680 · rust-lang/rfcs · GitHub

I haven't thought of it that way but it means the following can be done:

let shared = &MyStruct { value: 42};

let closure_1 = move || { println!("closure_1 {:?}", shared); };
let closure_2 = move || { println!("closure_2 {:?}", shared); };
let closure_3 = move || { println!("closure_3 {:?}", shared); };
let closure_4 = move || { println!("closure_4 {:?}", shared); };
let closure_5 = move || { println!("closure_5 {:?}", shared); };

That was very helpful, let to the following comment:

Which pointed to this solution:

let shared = MyStruct { value: 42};
let shared_fn = || { shared.clone() };

let closure_1 = move || { println!("closure_1 {:?}", shared_fn()); };
let closure_2 = move || { println!("closure_2 {:?}", shared_fn()); };
let closure_3 = move || { println!("closure_3 {:?}", shared_fn()); };
let closure_4 = move || { println!("closure_4 {:?}", shared_fn()); };
let closure_5 = move || { println!("closure_5 {:?}", shared_fn()); };
1 Like

One other technique you can make use of is a combination of shadowing in a new scope to avoid having to give things a dummy variable name. Described in detail here: Rust Atomics and Locks — Chapter 1. Basics of Rust Concurrency

3 Likes

Turns out this is possible with arena allocators. Opened a new topic here: