以下代码显示了两个闭包:c1、c2。c1由函数返回,c2在本地生成。c1 无法发送到线程。为什么?
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");
});
}
Run Code Online (Sandbox Code Playgroud)
在 Rust 中(例如 C++),每个闭包都有自己的匿名类型。
由于c2是本地的,编译器确切地知道它的概念类型是什么,因此可以知道它的所有特征,包括它的特征Send(因为它不会关闭任何东西!Send……因为它不会关闭任何东西)。
然而c1返回为dyn Fn,这意味着就编译器而言,它可以依赖的唯一特征是Fn。编译器仅具有本地可见性,这就是您告诉它所依赖的。
使其可发送的方法是断言并保证它是:
fn gen_closure() -> Box<dyn Fn(&str) + Send>
Run Code Online (Sandbox Code Playgroud)
在这种情况下,调用者可以依赖这个契约,并且如果返回的函数实际上不是,则被调用者将不会编译,Send例如
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);
});
}
Run Code Online (Sandbox Code Playgroud)
=>
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`
Run Code Online (Sandbox Code Playgroud)