从 Rust 中的异步闭包调用存储的闭包

Call stored closure from an async closure in rust

我有一个闭包可以改变在它之外设计的变量。我将如何调用这个从异步范围内修改状态的闭包?

我有以下代码(抽象的,以显示问题):

#[tokio::main]
async fn main() {
    let mut y = false;

    let mut incr = |z: bool| {
        y = z;
    };

    stream::iter(0..1).for_each(|_| async {
        incr(true);
    }).await;
});

产生以下内容:

error: captured variable cannot escape `FnMut` closure body
  --> src/main.rs:40:37
   |
36 |       let mut incr = |z: bool| {
   |           -------- variable defined here
...
40 |       stream::iter(0..1).for_each(|_| async {
   |  ___________________________________-_^
   | |                                   |
   | |                                   inferred to be a `FnMut` closure
41 | |         incr(true);
   | |         ---- variable captured here
42 | |     }).await;
   | |_____^ returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
   |
   = note: `FnMut` closures only have access to their captured variables while they are executing...
   = note: ...therefore, they cannot allow references to captured variables to escape

现在,我相信我明白了为什么会出现错误。我只是想不出解决这个问题的办法。

对于上下文:

  1. 我有一个 websocket 客户端,我正在从流中读取
  2. 每次我从流中接收数据时,我都在转换它
  3. 然后我需要用转换后的数据调用一个闭包以在其他地方使用——本质上就像 JavaScript 中的 EventEmitter。

我是不是走错了路?我是一名 JavaScript 开发人员,所以我必须在这里改变我的思维方式。

在rust中,通常需要使用某种同步机制。发送对普通变量的引用真的很困难。因此,您需要根据需要使用一些 ArcRwLockMutex。此外,您可能希望 move 将它们放入闭包中以避免生命周期问题。

在这种情况下,我认为 Arc<Cell> 可以:

use std::cell::Cell;
use std::sync::Arc;
use futures::StreamExt;
use futures::stream;


#[tokio::main]
async fn main() {
    let yy = Arc::new(Cell::new(false));
    let y = yy.clone();
    let incr = move |z: bool| {
        y.set(z);
    };

    stream::iter(0..1).for_each(|_| async {
        incr(true);
    }).await;
    
    println!("{yy:?}");
}

Playground

查看 sync 进一步阅读。 以及 cell documentation