提问人:Demurgos 提问时间:5/17/2021 最后编辑:Demurgos 更新时间:5/18/2021 访问量:686
如何使用异步闭包来捕获和接受引用
How to use an async closure both capturing and accepting references
问:
我正在尝试传递一个返回异步函数的闭包,并将此闭包称为异步谓词(类似于异步或其他高阶函数)。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_check1
compute_and_check2
-
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 上运行,但如果这是唯一的解决方案,我的代码每晚要求是可以接受的。
答: 暂无答案
评论
tokio 范围
的,但我从未使用过它,而且它看起来也没怎么使用,所以我不会太乐观。tokio-scoped
Arc
'static