从闭包调用异步函数

Sim*_* S. 4 asynchronous future rust async-await

我想要await一个async在迭代器中使用的闭包内的函数。需要闭包的函数在结构体实现中被调用。我无法弄清楚如何做到这一点。

这段代码模拟了我正在尝试做的事情:

struct MyType {}

impl MyType {
    async fn foo(&self) {
        println!("foo");

        (0..2).for_each(|v| {
            self.bar(v).await;
        });
    }

    async fn bar(&self, v: usize) {
        println!("bar: {}", v);
    }
}

#[tokio::main]
async fn main() {
    let mt = MyType {};
    mt.foo().await;
}
Run Code Online (Sandbox Code Playgroud)

显然,这将不起作用,因为闭包不是async,给我:

error[E0728]: `await` is only allowed inside `async` functions and blocks
 --> src/main.rs:8:13
  |
7 |         (0..2).for_each(|v| {
  |                         --- this is not `async`
8 |             self.bar(v).await;
  |             ^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks

Run Code Online (Sandbox Code Playgroud)

在寻找有关如何async从非函数调用函数的答案后async,我想到了这个:

error[E0728]: `await` is only allowed inside `async` functions and blocks
 --> src/main.rs:8:13
  |
7 |         (0..2).for_each(|v| {
  |                         --- this is not `async`
8 |             self.bar(v).await;
  |             ^^^^^^^^^^^^^^^^^ only allowed inside `async` functions and blocks

Run Code Online (Sandbox Code Playgroud)

但现在我正在解决终身问题:

error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
 --> src/main.rs:4:18
  |
4 |     async fn foo(&self) {
  |                  ^^^^^
  |                  |
  |                  this data with an anonymous lifetime `'_`...
  |                  ...is captured here...
...
8 |             tokio::spawn(async move {
  |             ------------ ...and is required to live as long as `'static` here
Run Code Online (Sandbox Code Playgroud)

这也不令我感到惊讶,因为据我所知,Rust 编译器无法知道线程将存活多长时间。鉴于此,生成的线程tokio::spawn可能比 type 的寿命更长MyType

我想出的第一个解决方法是创建bar一个关联函数,复制我在闭包中需要的所有内容并将其作为值传递给bar并调用它,MyType::bar(copies_from_self)但这变得越来越难看,因为有很多复制。这也感觉像是一种不知道生命周期如何运作的解决方法。

相反,我试图将futures::executor::block_onwhich 用于简单的任务,例如这篇文章中的任务:

tokio::spawn(async move {
    self.bar(v).await;
});
Run Code Online (Sandbox Code Playgroud)

但是当把它放在我使用第三方库1 的现实生活示例中时,它也使用了tokio,事情不再起作用。阅读文档后,我意识到这#[tokio::main]是一个最终将所有内容包装起来的宏,block_on因此通过这样做将嵌套block_on. 这可能是async调用的方法之一bar停止工作而没有任何错误或日志记录的原因(没有任何错误或日志记录(block_on因此不应该对代码产生任何影响)。我联系了那些说我可以使用的作者,for_each(|i| async move { ... })这让我更加困惑。

error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
 --> src/main.rs:4:18
  |
4 |     async fn foo(&self) {
  |                  ^^^^^
  |                  |
  |                  this data with an anonymous lifetime `'_`...
  |                  ...is captured here...
...
8 |             tokio::spawn(async move {
  |             ------------ ...and is required to live as long as `'static` here
Run Code Online (Sandbox Code Playgroud)

会导致编译错误

expected `()`, found opaque type`
Run Code Online (Sandbox Code Playgroud)

我认为这是有道理的,因为我现在要返回一个未来而不是(). 我对此的天真方法是尝试用这样的方法等待未来:

(0..2).for_each(|v| {
    futures::executor::block_on(self.bar(v));
});
Run Code Online (Sandbox Code Playgroud)

但这让我回到了第一个,导致了以下编译错误,我也认为这是有道理的,因为我现在又回到了awaitsync.

only allowed inside `async` functions and blocks` since the 
Run Code Online (Sandbox Code Playgroud)

这一发现也让我很难利用这里这里找到的答案。

毕竟这种货物崇拜编程的问题基本上是,是否有可能,如果有,我如何async迭代器中的闭包调用我的函数(最好不产生线程以避免生命周期问题)?如果这是不可能的,那么惯用的实现会是什么样的?


1这是使用的库/方法

Frx*_*rem 6

Iterator::for_each期望同步 clsure,因此您不能.await在其中使用(至少不能直接使用),也不能从中返回未来。

一种解决方案是只使用for循环而不是.for_each

for v in 0..2 {
    self.bar(v).await;
}
Run Code Online (Sandbox Code Playgroud)

更通用的方法是使用而不是迭代器,因为它们是异步等效的(并且流上的等效方法通常也是异步的)。这不仅适用for_each于大多数其他迭代器方法,而且适用于:

use futures::prelude::*;

futures::stream::iter(0..2)
    .for_each(|c| async move {
        self.bar(v).await;
    })
    .await;
Run Code Online (Sandbox Code Playgroud)