按函数关闭 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);
    };
}