Rust中如何在poll方法内调用带非静态引用的async函数?
问题:实现Future trait时的生命周期错误
我在实现Future trait时遇到了障碍。需要在自己的poll方法中调用一个第三方库的async函数,该函数仅接受&str参数,定义如下:
async fn make_query(query: &str) -> i32 { // Magic happens 5 }
无法修改这个函数的签名来获取参数所有权。
我定义了持有String的Executor结构体,希望调用make_query时引用它,并尝试为该结构体实现Future trait:
struct Executor { query: String, future: Mutex<Option<Pin<Box<dyn Future<Output = i32>>>>> } impl Future for Executor { type Output = i32; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { let mut lock = self.future.lock().unwrap(); if let None = *lock { // 此处出现错误 *lock = Some(Box::pin(make_query(&self.query))) } let future = lock.as_mut().unwrap(); pin!(future).poll(cx) } }
预期可以通过.await Executor自动执行查询:
#[tokio::main] async fn main() { let result = Executor { query: "foo".to_string(), future: Mutex::new(None) }.await; println!("Result is {result}"); }
但出现生命周期错误,提示Mutex要求传入的引用必须是'static,错误信息如下:
error: lifetime may not live long enough --> src/main.rs:23:26 | 16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | - let's call the lifetime of this reference `'1` ... 23 | *lock = Some(Box::pin(make_query(&self.query))) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` error[E0597]: `self` does not live long enough --> src/main.rs:23:47 | 16 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ---- binding `self` declared here ... 23 | *lock = Some(Box::pin(make_query(&self.query))) | ---------------------^^^^-------- | | | | | borrowed value does not live long enough | cast requires that `self` is borrowed for `'static` ... 29 | } | - `self` dropped here while still borrowed
无法修改poll的函数签名,需要解决这个生命周期问题,或者找到更合适的实现方式。
解决方案
移除结构体中的future字段,简化Executor定义:
struct Executor { query: String }
不再直接实现Future trait,转而实现IntoFuture trait(使用.await时会隐式调用该trait)。通过async move {}转移Executor的所有权,这样就可以安全地引用内部的query字符串:
注意:当前
type IntoFuture = impl Future是不稳定特性,因此使用Pin<Box<dyn Future>>作为返回类型
impl IntoFuture for Executor { type Output = i32; type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>; fn into_future(self) -> Self::IntoFuture { Box::pin(async move { make_query(&self.query).await }) } }
现在可以正常对Executor调用.await:
#[tokio::main] async fn main() { let res = Executor { query: "foo".into() }.await; println!("Result is {res}"); // 5 }
内容的提问来源于stack exchange,提问作者hjpev
相关产品推荐
相关产品推荐

