Summary
During the discussion of the Custom Panic Handler RFC, the idea came up if we could Sync (some) closures. So to start the discussion, I’m writing this Pre-RFC.
Motivation
In multithreaded environments, it may be useful to share callbacks between threads. And callbacks are very commonly defined as closures. The following program tries to share a very simple closure between threads (that don’t even run concurrently):
use std::thread;
use std::sync::Arc;
fn main() {
let x : &'static Fn(u32) = |l| { /* do something with l */ };
let y = &Arc::new(x); // NOPE, this won't compile
for v in 1..4 {
println!("{:?}", thread::spawn(move|| {
y(v);
}).join())
}
}
This will fail, because the trait core::marker::Sync is not implemented for the type core::ops::Fn(u32) [E0277].
Now, there is nothing within x that would preclude it from being Sync, but rustc doesn’t know that, so it balks.
Detailed Design
The compiler should already know whether a closure is immutable (because of the Fn declaration), and since it already keeps track of captured variables (so it can size the closure-struct), it should be simple enough to determine if a closure is eligible for Sync during compilation.
On detecting such closures, the compiler would then implicitly add a Sync trait bound. The error message for missing Sync on closures should be extended to suggest making the closure an Fn and/or show the captured state.
Drawbacks
- It may not be obvious from the code if a closure captures any state. A good error message should remediate this.
- It will make the compiler slower, because it has to do the aforementioned additional check for each closure definition.
Alternatives
- Leave things as they are
- Require the user to state their intention by adding
Sync as a trait bound manually when defining the closure. This would reduce the burden on the compiler (since the check would only run if a closure is marked as Sync
Unresolved questions
All of them. This is a Pre-RFC, remember?