In the following example, I'd like to force x to be capture by-move in the inner closure:
use std::thread;
fn main() {
thread::scope(|s| {
for x in 0u32..10 {
s.spawn(|| {
println!("{}", {x});
});
}
});
}
It seems like there's currently no way to do that. I can use move but that forces all captures to be by-move, and the real code where this came up wants to capture a bunch of other things by-ref. (I know how to make it work with move, but it requires a bunch of boilerplate before the closure to explicitly turn some things into references, so it's not pretty.)
So... what could be a good way to allow by-move captures here? Do some of the proposals for clone-on-capture provide enough control to do this? Should we have a Move type in core that can aid in this? It's still a bit awkward even with such a type though:
use std::thread;
struct Move<T>(T);
fn main() {
thread::scope(|s| {
for x in 0u32..10 {
let x = Move(x);
s.spawn(|| {
let Move(x) = {x};
println!("{}", x);
});
}
});
}
As a potential language extension, I could see move(x,y,z) || ... for "capture these by move/copy, but use the normal inference for any others".
Taking that a step further, move(x: x.clone(), y: some_expr, z) || ... could improve ergonomics, though the only real advantage is that you wouldn't need temporaries for those captures that would be fine with the inferred borrow.
Crates would run into the same issue that manual users would: There are workarounds that involve being explicit about all captures, i.e. you use a move closure and explicitly define local variables holding references for capture-by-reference things, but there’s no way to do this without mentioning everything that’s captured.
There’s also no way to deduce all the implicit captures. Only the compiler can do that, with a lot of extra information that’s not syntactic. You can’t really syntactically differentiate access to local variables vs. global items (e.g. is X.foo() referring to some let X = … or some static X: … = …;? Is f() referring to some fn f() {…} or some let f = || …;?) and you can’t syntactically differentiate different forms of access (does foo.bar() access foo by-value or by-reference [mutable or immutable]?)
I see. And there isn't any roundabout way of forcing the move of certain parameters into a non-move closure, but without making it FnOnce?
One idea I have is a macro that expands to: two instances of the closure, one unreachable -- quick question, it there a way to make an unreachable closure not consume the things it captures? -- which should ensure that the macro doesn't move out of a variable, and a reachable closure which uses unsafe code to repeatedly duplicate and forget the value from behind a pointer captured by reference when the closure is called, and Drop it when the closure is dropped.
Now that I wrote it out I realized that it probably doesn't work, but maybe it's food for thought for someone who comes up with a working version...?