Send Approximation

一些async fn状态机可以安全地越过线程发送,而其他则不能。判断一个async fn Future是不是Send,由非Send类型是否越过一个.await据点决定的。当可以越过了.await据点,编译器会尽力去估计这个是/否。但是今天的许多地方,这种分析都太保守了。

例如,考虑一个简单的非Send类型,也许包含一个Rc

  1. use std::rc::Rc;
  2. #[derive(Default)]
  3. struct NotSend(Rc<()>);

类型NotSend的变量可以短暂的,像暂时变量一样出现在async fns,即使说async fn返回的Future类型结果,一定要是Send

  1. # use std::rc::Rc;
  2. # #[derive(Default)]
  3. # struct NotSend(Rc<()>);
  4. async fn bar() {}
  5. async fn foo() {
  6. NotSend::default();
  7. bar().await;
  8. }
  9. fn require_send(_: impl Send) {}
  10. fn main() {
  11. require_send(foo());
  12. }

但是,如果我们对foo修改一下,将NotSend存储在一个变量中,那么此示例不再编译:

  1. # use std::rc::Rc;
  2. # #[derive(Default)]
  3. # struct NotSend(Rc<()>);
  4. async fn foo() {
  5. let x = NotSend::default();
  6. bar().await;
  7. }
  1. error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
  2. --> src/main.rs:15:5
  3. |
  4. 15 | require_send(foo());
  5. | ^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
  6. |
  7. = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
  8. = note: required because it appears within the type `NotSend`
  9. = note: required because it appears within the type `{NotSend, impl std::future::Future, ()}`
  10. = note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]`
  11. = 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, ()}]>`
  12. = note: required because it appears within the type `impl std::future::Future`
  13. = note: required because it appears within the type `impl std::future::Future`
  14. note: required by `require_send`
  15. --> src/main.rs:12:1
  16. |
  17. 12 | fn require_send(_: impl Send) {}
  18. | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  19. error: aborting due to previous error
  20. For more information about this error, try `rustc --explain E0277`.

此错误是正确的。如果我们将x存储到一个变量中,在.await搞完之前,这个变量都不会 drop,而此时,这个async fn有可能在其他线程上运行。因Rc不是Send,让它越过线程传播是不合理的。一个简单的解决方案是在.await之前,就对Rc进行drop,但是很遗憾,今天这个还不能用。

为了成功解决此问题,您可能必须引入一个封装了所有非Send的变量。这使编译器更容易知道这些变量,不越过.await据点。

  1. # use std::rc::Rc;
  2. # #[derive(Default)]
  3. # struct NotSend(Rc<()>);
  4. async fn foo() {
  5. {
  6. let x = NotSend::default();
  7. }
  8. bar().await;
  9. }