Send

一些 async fn 状态机可以安全地跨线程发送,而另一些则不是。 async fn Future 是否为 Send,取决于是否跨 .await 持有非 Send 类型。 编译器会尽可能地预估出值可能通过 .await 的时间点, 但现在这种分析在许多地方都太过于保守。

比如,考虑一种简单的 non-Send 类型,也许只是一个包含 Rc 的类型:

#![allow(unused)]
fn main() {
use std::rc::Rc;

#[derive(Default)]
struct NotSend(Rc<()>);
}

即使 async fn 返回的结果必须是 Send 类型,但 Non-Send 类型变量, 也可短暂地作为临时变量在 async fn 里使用:

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    NotSend::default();
    bar().await;
}

fn require_send(_: impl Send) {}

fn main() {
    require_send(foo());
}

一旦我们将 NotSend 存储在变量里,这个例子就无法通过编译了:

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    let x = NotSend::default();
    bar().await;
}
fn require_send(_: impl Send) {}
fn main() {
   require_send(foo());
}
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
  --> src/main.rs:15:5
   |
15 |     require_send(foo());
   |     ^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
   |
   = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
   = note: required because it appears within the type `NotSend`
   = note: required because it appears within the type `{NotSend, impl std::future::Future, ()}`
   = note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]`
   = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]>`
   = note: required because it appears within the type `impl std::future::Future`
   = note: required because it appears within the type `impl std::future::Future`
note: required by `require_send`
  --> src/main.rs:12:1
   |
12 | fn require_send(_: impl Send) {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.

这个报错是正确的。如果我们将 x 存储在一个变量里,在 .await 之后它才会被删除,而此时 async fn 可能在其它的进程中运行。 因为 Rc 不是 Send,它不能安全地在线程间传输。一个简单的解决办法是, 在 .await 之前删除 Rc,但遗憾的是目前无法这么做。

你可以通过使用一个代码块({})来包裹住所有的 non-Send 变量,这可解决这个问题。 这样就很方便的告知编译器,这些变量在 .await 前就被丢弃了。

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    {
        let x = NotSend::default();
    }
    bar().await;
}
fn require_send(_: impl Send) {}
fn main() {
   require_send(foo());
}