如何实现跨await点持有引用的异步闭包?稳定替代方案求助
当你尝试编写捕获引用并在await后使用的异步闭包时,会遇到生命周期错误——编译器无法推断出输入引用的生命周期足够覆盖异步块的执行周期。原错误代码及报错如下:
错误示例代码
use std::time::Duration; use tokio::time::sleep; fn main() { let closure = |v: &u64| async move { sleep(Duration::from_secs(1)).await; println!("{}", v); }; }
报错信息
error: lifetime may not live long enough
--> src/main.rs:5:29
|
5 | let closure = |v: &u64| async move {
| _______________--_^
| | | |
| | | return type of closure[async block@src/main.rs:5:29: 8:6]contains a lifetime'2
| | let's call the lifetime of this reference'1
6 | | sleep(Duration::from_secs(1)).await;
7 | | println!("{}", v);
8 | | };
| |^ returning this value requires that'1must outlive'2
虽然 nightly Rust 的async_closure特性可以自动解决这个问题,但该特性尚未稳定。以下是两种稳定Rust下的替代方案:
方案1:显式标注返回Future的生命周期
通过为闭包的返回类型指定impl Future<Output = ()> + '_,告诉编译器异步块的生命周期依赖于输入引用的生命周期,从而满足'1必须长于'2的要求:
use std::time::Duration; use tokio::time::sleep; use std::future::Future; fn main() { let closure = |v: &u64| -> impl Future<Output = ()> + '_ { async move { sleep(Duration::from_secs(1)).await; println!("{}", v); } }; }
方案2:获取值的所有权(若类型允许)
如果被引用的类型实现了Copy或可以克隆,直接复制/克隆值到async块中,消除对引用的依赖,从根源上解决生命周期问题:
use std::time::Duration; use tokio::time::sleep; fn main() { let closure = |v: &u64| { let v = *v; // 利用u64的Copy特性复制值 async move { sleep(Duration::from_secs(1)).await; println!("{}", v); } }; }
如果类型不是Copy但实现了Clone,可以替换为let v = v.clone();。
内容的提问来源于stack exchange,提问作者Heinzi

