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 fix this as follows:
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();
```
Playground
So here, we move ‘all used variables’ but turn settings into a reference beforehand, so it is borrowed instead. (Pedantically: we ‘move the reference’ but that is exactly what a ‘borrow’ is.)
There is also a second possibility: Borrowing all variables (and moving nothing).
This possibility works in many contexts but not here. (Because here, we have to capture score by value as it will not be there during the next iteration of the for loop and the closure passed to scope.spawn will outlive this).
Taken from the documentation of rayon::scope
use rayon;
fn main() {
let ok: Vec<i32> = vec![1, 2, 3];
rayon::scope(|s| {
let bad: Vec<i32> = vec![4, 5, 6];
s.spawn(|_| {
// Transfer ownership of `bad` into a local variable (also named `bad`).
// This will force the closure to take ownership of `bad` from the environment.
let bad = bad;
println!("ok: {:?}", ok); // `ok` is only borrowed.
println!("bad: {:?}", bad); // refers to our local variable, above.
});
s.spawn(|_| println!("ok: {:?}", ok)); // we too can borrow `ok`
});
}
Playground