Send Approximation
一些async fn状态机可以安全地越过线程发送,而其他则不能。判断一个async fn Future是不是Send,由非Send类型是否越过一个.await据点决定的。当可以越过了.await据点,编译器会尽力去估计这个是/否。但是今天的许多地方,这种分析都太保守了。
例如,考虑一个简单的非Send类型,也许包含一个Rc:
use std::rc::Rc;#[derive(Default)]struct NotSend(Rc<()>);
类型NotSend的变量可以短暂的,像暂时变量一样出现在async fns,即使说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存储在一个变量中,那么此示例不再编译:
# use std::rc::Rc;# #[derive(Default)]# struct NotSend(Rc<()>);async fn foo() {let x = NotSend::default();bar().await;}
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 errorFor more information about this error, try `rustc --explain E0277`.
此错误是正确的。如果我们将x存储到一个变量中,在.await搞完之前,这个变量都不会 drop,而此时,这个async fn有可能在其他线程上运行。因Rc不是Send,让它越过线程传播是不合理的。一个简单的解决方案是在.await之前,就对Rc进行drop,但是很遗憾,今天这个还不能用。
为了成功解决此问题,您可能必须引入一个封装了所有非Send的变量。这使编译器更容易知道这些变量,不越过.await据点。
# use std::rc::Rc;# #[derive(Default)]# struct NotSend(Rc<()>);async fn foo() {{let x = NotSend::default();}bar().await;}
