Asynchronous 为什么跨等待点持有非发送类型会导致未来不发送?

Asynchronous 为什么跨等待点持有非发送类型会导致未来不发送?,asynchronous,rust,future,Asynchronous,Rust,Future,在的文档中,有一个很好的例子,说明了Rc之类的东西是如何不发送的,因为在两个不同的线程中克隆/删除会导致引用计数不同步 然而,不太清楚的是,为什么在异步fn中的等待点上保持对非发送类型的绑定会导致生成的未来也是非发送。当编译器在异步手册中过于保守时,我能够找到一种解决方法,但它并没有回答我在这里提出的问题 也许有人可以用一个例子来说明这一点,说明为什么在未来的中键入一个非发送是可以的,但是在等待过程中按住它就不行了?在异步函数中使用等待时,编译器会在后台构建一个状态机。每个.await都会引入一

在的文档中,有一个很好的例子,说明了Rc之类的东西是如何不发送的,因为在两个不同的线程中克隆/删除会导致引用计数不同步

然而,不太清楚的是,为什么在
异步fn
中的
等待
点上保持对非
发送
类型的绑定会导致生成的未来也是非
发送
。当编译器在异步手册中过于保守时,我能够找到一种解决方法,但它并没有回答我在这里提出的问题


也许有人可以用一个例子来说明这一点,说明为什么在
未来的
中键入一个非
发送
是可以的,但是在
等待
过程中按住它就不行了?

在异步函数中使用
等待
时,编译器会在后台构建一个状态机。每个
.await
都会引入一个新的状态(当它等待某件事情时),中间的代码是状态转换(也称为任务),它将根据一些外部事件(例如来自IO或计时器等)触发

每个任务都被安排由异步运行时执行,异步运行时可以选择使用与前一个任务不同的线程。如果在线程之间发送状态转换不安全,则生成的
Future
也不是
Send
,因此,如果尝试在多线程运行时执行该转换,则会出现编译错误

未来的
不发送
是完全可以的,这只意味着您只能在单线程运行时执行它


也许有人可以用一个例子来说明这一点,说明为什么在
未来
中键入一个非
发送
是可以的,但在
等待
中按住它就不行了

考虑以下简单示例:

async fn add_votes(current: Rc<Cell<i32>>, post: Url) {
    let new_votes = get_votes(&post).await;
    *current += new_votes;
}
在这种情况下,状态机将如下所示:

enum AddVotes {
    Initial {
        current: i32,
        post: Url,
    },
    WaitingForGetVotes { 
        current: i32,
        fut: GetVotesFut,
    },
}

Rc
不会在状态机中捕获,因为它是在状态转换(任务)中创建和删除的,所以整个状态机(aka
Future
)仍然是
Send

wait
中保存的所有内容都需要在将来存储,因为函数在此时返回,存储其状态。如果你在将来存储的东西不是
Send
,那么未来本身就不可能是
Send
。现在你这么说似乎很直观。那么,在什么情况下,我将非
Send
的内容作为参数传递给
异步fn
,该fn已被调用以生成
未来
,但尚未被轮询。我猜这些参数也存储在生成的
Future
?中,这取决于
async fn
如何使用它。如果
async fn
await
-ing之前使用它,那么未来将不需要存储它,因此应该发送。@masklin即使async fn在第一次
await
之前使用参数,我认为未来仍然需要存储它们。调用async fn时,它会立即返回future,而且在future上调用
poll()
之前,函数体不会开始执行。是的,这是
async fn foo()
fn bar()->impl future
之间的区别之一:
bar
可以在被调用时立即工作,而
foo
必须存储其所有参数并等待轮询。
impl AddVotes {
    fn new(current: Rc<Cell<i32>>, post: Url) {
        AddVotes::Initial { current, post }
    }

    fn poll(&mut self) -> Poll {
        match self {
            AddVotes::Initial(state) => {
                let fut = get_votes(&state.post);
                *self = AddVotes::WaitingForGetVotes {
                     current: state.current,
                     fut
                }
                Poll::Pending
            }
            AddVotes::WaitingForGetVotes(state) => {
                if let Poll::Ready(votes) = state.fut.poll() {
                    *state.current += votes;
                    Poll::Ready(())
                } else {
                    Poll::Pending
                }
            }
        }
    }
}
async fn add_votes(current: i32, post: Url) -> i32 {
    let new_votes = get_votes(&post).await;

    // use an Rc for some reason:
    let rc = Rc::new(1);
    println!("rc value: {:?}", rc);

    current + new_votes
}
enum AddVotes {
    Initial {
        current: i32,
        post: Url,
    },
    WaitingForGetVotes { 
        current: i32,
        fut: GetVotesFut,
    },
}