按函数关闭 return 和在 Rust 中本地生成之间有什么区别?
What is the diffrence between closure return by function and generated locally in rust?
以下代码显示了两个闭包: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");
});
}
在 Rust 中(例如在 C++ 中)每个闭包都有自己的匿名类型。
由于 c2
是本地的,编译器确切地知道它的 concete 类型是什么,因此可以知道它的所有特征,包括它是 Send
(因为它没有关闭 !Send
...因为它没有关闭任何东西)。
但是 c1
返回为 dyn Fn
,这意味着就编译器而言 它唯一可以依赖的特性是 Fn
.编译器仅具有本地可见性,这就是您要告诉它依赖的内容。
使其可发送的方法是断言并保证它是:
fn gen_closure() -> Box<dyn Fn(&str) + Send>
在那种情况下,调用者可以依赖这个契约,如果返回的函数实际上不是,被调用者将不会编译 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);
});
}
=>
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`
为了补充 Masklinn 的回答,我想再举一个例子:
如果您将 return 类型更改为 impl Fn(&str)
,那么它也会起作用。因为函数将针对具体类型实例化,所以 returned 类型将被称为 Send
.
fn gen_closure() -> impl Fn(&str) {
return |s: &str| {
println!("gen_closure: {}", s);
};
}
以下代码显示了两个闭包: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");
});
}
在 Rust 中(例如在 C++ 中)每个闭包都有自己的匿名类型。
由于 c2
是本地的,编译器确切地知道它的 concete 类型是什么,因此可以知道它的所有特征,包括它是 Send
(因为它没有关闭 !Send
...因为它没有关闭任何东西)。
但是 c1
返回为 dyn Fn
,这意味着就编译器而言 它唯一可以依赖的特性是 Fn
.编译器仅具有本地可见性,这就是您要告诉它依赖的内容。
使其可发送的方法是断言并保证它是:
fn gen_closure() -> Box<dyn Fn(&str) + Send>
在那种情况下,调用者可以依赖这个契约,如果返回的函数实际上不是,被调用者将不会编译 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);
});
}
=>
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`
为了补充 Masklinn 的回答,我想再举一个例子:
如果您将 return 类型更改为 impl Fn(&str)
,那么它也会起作用。因为函数将针对具体类型实例化,所以 returned 类型将被称为 Send
.
fn gen_closure() -> impl Fn(&str) {
return |s: &str| {
println!("gen_closure: {}", s);
};
}