如何使用异步闭包来捕获和接受引用

How to use an async closure both capturing and accepting references

提问人:Demurgos 提问时间:5/17/2021 最后编辑:Demurgos 更新时间:5/18/2021 访问量:686

问:

我正在尝试传递一个返回异步函数的闭包,并将此闭包称为异步谓词(类似于异步或其他高阶函数)。Future<Output=bool>.filter

此谓词接收其输入作为引用。我找到了如何为不捕获其环境的纯谓词实现它:

type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;

////////////////////////////////////////////////////////////////////////////////
// 1 -> Closure only uses the inner argument                                  //
////////////////////////////////////////////////////////////////////////////////

/// Run the computation and check that its output is not empty
fn run1<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
  async move {
    let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
      b.len() > 0 // Only uses the inner argument
    }));
    let is_ok1 = is_ok.await;
    dbg!(is_ok1);
  }
}

/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check1<F>(check: F) -> bool
  where
    F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r, bool>
{
  let bytes = [0u8; 128];
  let is_ok = check(&bytes).await;
  drop(bytes);
  is_ok
}

游乐场链接

仅支持非捕获闭包是相当有限的。我想使用一个捕获其环境的闭包。通过更改函数的边界,我能够传递一个使用其环境的闭包 - 但不使用其输入:compute_and_check

type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;

////////////////////////////////////////////////////////////////////////////////
// 2 -> Closure only uses the outer argument                                  //
////////////////////////////////////////////////////////////////////////////////

/// Run the computation and assume that its output is not empty if `expected` is not empty
fn run2<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
  async move {
    let is_ok = compute_and_check2(|b: &[u8]| Box::pin(async move {
      expected.len() > 0 // Only uses the environment
    }));
    let is_ok2 = is_ok.await;
    dbg!(is_ok2);
  }
}

/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check2<'a, F>(check: F) -> bool
  where
    F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'a, bool>
{
  let bytes = [0u8; 128];
  let is_ok = check(&bytes).await;
  drop(bytes);
  is_ok
}

游乐场链接

我可以编写一个实现,其中闭包使用其输入,以及闭包使用其环境的实现。但不是同时两者。

我怎样才能接受一个同时使用引用作为其输入和环境的 Future 生成闭包?

我想写的是这样的:

type BoxFuture<'a, Out> = Pin<Box<dyn Future<Output=Out> + 'a + Send>>;

////////////////////////////////////////////////////////////////////////////////
// 3 -> Closure uses both the inner and outer arguments                       //
////////////////////////////////////////////////////////////////////////////////

/// Run the computation and check its output is the provided expected value
fn run3<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
  async move {
    let is_ok = compute_and_check3(|b: &[u8]| Box::pin(async move {
      b == expected // Uses both the input and environment
    }));
    let is_ok2 = is_ok.await;
    dbg!(is_ok2);
  }
}

/// Compute some bytes (may be complex / use async), then check their validity
/// with a user-supplied function, finally do some clean-up and return.
async fn compute_and_check3<'a, F>(check: F) -> bool
  where
    F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r + 'a, bool>
{
  let bytes = [0u8; 128];
  let is_ok = check(&bytes).await;
  drop(bytes);
  is_ok
}

游乐场链接

此代码无法编译,因为我要求关闭返回,但这不是合法语法:BoxFuture<'r + 'a, bool>

error[E0226]: only a single explicit lifetime bound is permitted
  --> src/main.rs:89:51
   |
89 |     F: for<'r> FnOnce(&'r [u8]) -> BoxFuture<'r + 'a, bool>
   |                                                   ^^

据我了解,我问题的核心是我需要限制我的高级特质束缚。我想说的不是“对于任何”,而是“对于任何没有寿命的人”,但我不明白如何写下来。'r'r'a

我尝试对我的类型别名使用两个生存期和限制,或者定义一个辅助特征,但我没有设法解决这个问题,因为我未能对 HRTB 生存期应用限制。BoxFuture

为了完整起见,以下是我在将最终闭包传递给(仅输入)和(仅环境)时遇到的错误:compute_and_check1compute_and_check2

  • 使用compute_and_check1(仅输入)操场链接

    error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
      --> src/main.rs:17:67
       |
    17 |       let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
       |  ___________________________________________________________________^
    18 | |       b == expected
    19 | |     }));
       | |_____^
       |
    note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 15:9...
      --> src/main.rs:15:9
       |
    15 | fn run3<'a>(expected: &'a [u8]) -> impl Future<Output=()> + 'a {
       |         ^^
    note: ...so that the types are compatible
      --> src/main.rs:17:67
       |
    17 |       let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
       |  ___________________________________________________________________^
    18 | |       b == expected
    19 | |     }));
       | |_____^
       = note: expected `(&[u8], &[u8])`
                  found `(&[u8], &'a [u8])`
    note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the body at 17:36...
      --> src/main.rs:17:36
       |
    17 |       let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
       |  ____________________________________^
    18 | |       b == expected
    19 | |     }));
       | |______^
    note: ...so that the expression is assignable
      --> src/main.rs:17:47
       |
    17 |       let is_ok = compute_and_check1(|b: &[u8]| Box::pin(async move {
       |  _______________________________________________^
    18 | |       b == expected
    19 | |     }));
       | |______^
       = note: expected `Pin<Box<dyn Future<Output = bool> + Send>>`
                  found `Pin<Box<dyn Future<Output = bool> + Send>>`
    
  • 使用 compute_and_check2(仅限环境)操场链接

    error: lifetime may not live long enough
      --> src/main.rs:17:47
       |
    17 |       let is_ok = compute_and_check2(|b: &[u8]| Box::pin(async move {
       |  ________________________________________-____-_^
       | |                                        |    |
       | |                                        |    return type of closure is Pin<Box<(dyn Future<Output = bool> + Send + '2)>>
       | |                                        let's call the lifetime of this reference `'1`
    18 | |       b == expected
    19 | |     }));
       | |______^ returning this value requires that `'1` must outlive `'2`
    

我还研究了每晚unboxed_closure功能,但没有设法解决我的问题。我希望我的代码在稳定的 Rust 上运行,但如果这是唯一的解决方案,我的代码每晚要求是可以接受的。

异步 Rust 闭包生存

评论

0赞 Peter Hall 5/18/2021
我不确定您是否可以在不使用“作用域”运行时的情况下完成这项工作。有 tokio 范围的,但我从未使用过它,而且它看起来也没怎么使用,所以我不会太乐观。
0赞 Peter Hall 5/18/2021
不过,您可以使用引用计数来执行大致相同的操作:play.rust-lang.org/...
0赞 Demurgos 5/18/2021
感谢您的回复。我问题的根本原因是我实际上想要这样的东西,但这个库没有维护。一般来说,结构化的并发性对 Tokio 来说是一个难题。关于使用 ,它的工作原理是使环境 :在这种情况下,它很好,但它仍然是一种解决方法,需要在不需要的地方引入引用计数。tokio-scopedArc'static

答: 暂无答案