Send Approximation

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

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


    #![allow(unused_variables)]
    fn main() {
    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存储在一个变量中,那么此示例不再编译:


    #![allow(unused_variables)]
    fn main() {
    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 error
    
    For more information about this error, try `rustc --explain E0277`.
    

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

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


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