Is there a way to have a Rust closure that moves only some variables into it?

Yes, it is possible to move only one or some variables into a closure (rather than all or none).

Yes, this can be used to "circumvent" reference counting.

I found an answer in the documentation of rayon::scope that turns out to be exactly about this problem: 'Accessing the stack data [from within a scoped threads scope]'. That page also has an example that is clearer than the pseudocode in this question.

It turns out that you can either:

  • Use a move closure but refer to variables in the outer scope by shadowing them with a reference, therefore capturing them by reference rather than by value, using let settings = &settings:

    crossbeam::scope(|scope| {
        let settings = &settings; // refer to outer variable by reference
        for score in 0..MAX_FEASIBLE_SCORE {
            scope.spawn(move |_| {
                let work_result = do_cool_computation(settings, score);
                println!("{:?}", work_result);
            });
        }
    })
    .unwrap();
    
  • Use a normal closure, and only move the required variables by shadowing them inside the closure using let score = score:

    crossbeam::scope(|scope| {
        for score in 0..MAX_FEASIBLE_SCORE {
            scope.spawn(|_| {
                let score = score; // capture only score
                let work_result = do_cool_computation(&settings, score);
                println!("{:?}", work_result);
            });
        }
    })
    .unwrap();
    

The closure! macro gives the ability to selectively reference, move, or clone variables into a closure.

Example taken from the docs:

use closure::closure;

let string = "move".to_string();
let x = 10;
let mut y = 20;
let rc = Rc::new(5);

let closure = closure!(move string, ref x, ref mut y, clone rc, |arg: i32| {
    ...
});

Variables that are captured but not listed default to being moved.