Send 估计
有的async fn状态机在线程间传递时是安全的,有的不是。一个async fn的future是否具有Sendtrait是根据 是否有 跨越带.await的语句的 非Send类型来判断的。编译器会尽可能估计这些值可能跨越.await的时点,但是在今天看来,这种分析显得太过保守了。
举个例子,考虑一个简单的非Send类型,比如包含了一个Rc的类型:
#![allow(unused)] fn main() { use std::rc::Rc; #[derive(Default)] struct NotSend(Rc<()>); }
NotSend类型的变量在async fn中可以作为临时变量短时间出现,即便async fn返回的最终的Future类型必须是Send:
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()); }
但是,如果改变foo来将NotSend存储在一个变量中,这个例子就不能编译了:
However, if we change foo to store NotSend in a variable, this example no
longer compiles:
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给drop掉,不过这个目前不行。
为了解决这个问题,可以通过一个块作用域来把非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()); }