The following codes show two closures: c1, c2.
c1 is returned by a function, c2 is generated locally. c1 can not be sent to a thread. Why?
fn gen_closure() -> Box<dyn Fn(&str)> {
return Box::new(|s: &str| {
println!("gen_closure: {}", s);
});
}
fn main() {
let c1 = gen_closure();
let c2 = Box::new(|s: &str| {
println!("main_closure: {}", s);
});
/* `dyn for<'r> Fn(&'r str)` cannot be sent between threads safely
the trait `Send` is not implemented for `dyn for<'r> Fn(&'r str)`
required because of the requirements on the impl of `Send` for `Unique<dyn for<'r> Fn(&'r str)>`
required because it appears within the type `[closure@src/main.rs:13:24: 15:6]` */
std::thread::spawn(move || {
(c1)("c1");
});
/* This is okay */
std::thread::spawn(move || {
(c2)("c2");
});
}
>Solution :
In Rust (as in e.g. C++) every closure has its own anonymous type.
Since c2 is a local, the compiler knows exactly what its concete type is, and thus can know all its traits, including that it’s Send (because it closes over nothing that’s !Send… since it’s not closing over anything).
However c1 is returned as dyn Fn, meaning as far as the compiler is concerned the only trait it can rely on is Fn. The compiler only has local visibility, and that’s what you’re telling it to rely on.
The way to make it sendable is to guaranteed that it is:
fn gen_closure() -> Box<dyn Fn(&str) + Send>
In that case, the caller can rely on this contract, and the callee will not compile if the returned function is not actually Send e.g.
fn gen_closure() -> Box<dyn Fn(&str) + Send> {
let rc = std::rc::Rc::new(1);
return Box::new(move |s: &str| {
println!("gen_closure: {} {}", s, rc);
});
}
=>
error[E0277]: `Rc<i32>` cannot be sent between threads safely
--> src/main.rs:3:12
|
3 | return Box::new(move |s: &str| {
| _____________^________-
| | ____________|
| ||
4 | || println!("gen_closure: {} {}", s, rc);
5 | || });
| ||_____-^ `Rc<i32>` cannot be sent between threads safely
| |______|
| within this `[closure@src/main.rs:3:21: 5:6]`
|
= help: within `[closure@src/main.rs:3:21: 5:6]`, the trait `Send` is not implemented for `Rc<i32>`
= note: required because it appears within the type `[closure@src/main.rs:3:21: 5:6]`
= note: required for the cast to the object type `dyn for<'r> Fn(&'r str) + Send`