You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中存储多个Future并执行?Unpin trait报错解决

解决async trait返回Future的Unpin错误问题

背景说明

使用Rust nightly版本,通过静态分发使用原生async trait(不依赖async-traits crate),已定义如下trait及实现:

定义的Trait

pub trait MakesWidgets {
  async fn make_widget(&self) -> Result<Widget, String>;
}

Trait实现

impl MakesWidgets for DefaultWidgetMaker {
  async fn make_widget(&self) -> Result<Widget, String> {
    self.make_widget_internal().await
  }
}

待实现的批量方法

尝试在DefaultWidgetMaker上实现批量创建Widget的方法,逻辑是存储部分Future后续执行:

impl DefaultWidgetMaker {
  pub async fn make_widgets(&self, requests: Vec<bool>) -> Vec<Result<Widget, String>> {
    let n = requests.len();
    let mut responses = Vec::with_capacity(n);
    for _ in 0..n { responses.push(None); }
    let mut pending = Vec::with_capacity(n);
    for _ in 0..n { pending.push(None); }
    for (i, should_block) in requests.iter().enumerate() {
      let future = self.make_widget();
      if *should_block {
        responses[i] = Some(future.await);
      } else {
        pending[i] = Some(future);
      }
    }
    for (i, maybe_future) in pending.iter().enumerate() {
      if let Some(ref mut future) = maybe_future {
        responses[i] = Some(future.await);
      }
    }
    responses.map(|x| x.unwrap())
  }
}

遇到的错误

执行后出现以下编译错误:

#0 4.372 error[E0277]: `impl futures_util::Future<Output = std::result::Result<Widget, std::string::String>>` cannot be unpinned
#0 4.372   --> src/xxxxxx/xxxxxxxxx.rs:65:38
#0 4.372    |
#0 4.372 65 |                 let response = future.await;
#0 4.372    |                                      ^^^^^^
#0 4.372    |                                      |
#0 4.372    |                                      the trait `Unpin` is not implemented for `impl futures_util::Future<Output = std::result::Result<Widget, std::string::String>>`
#0 4.372    |                                      help: remove the `.await`
#0 4.372    |
#0 4.372    = note: consider using `Box::pin`
#0 4.372    = note: required for `&mut impl futures_util::Future<Output = std::result::Result<Widget, std::string::String>>` to implement `futures_util::Future`
#0 4.372    = note: required for `&mut impl futures_util::Future<Output = std::result::Result<Widget, std::string::String>>` to implement `std::future::IntoFuture`

问题原因

async trait返回的impl Future默认未实现Unpin trait,而当你将Future存储到容器后再通过可变引用执行.await时,Rust要求该Future必须实现Unpin(避免Future在执行过程中被移动导致状态错乱)。

解决方法

方法1:用Box::pin装箱并固定Future

这是最通用的解决方案,将未立即执行的Future装箱为Pin<Box<dyn Future>>,该类型自动实现Unpin:

impl DefaultWidgetMaker {
  pub async fn make_widgets(&self, requests: Vec<bool>) -> Vec<Result<Widget, String>> {
    let n = requests.len();
    let mut responses = vec![None; n];
    let mut pending = vec![None; n];
    
    for (i, &should_block) in requests.iter().enumerate() {
      let future = self.make_widget();
      if should_block {
        responses[i] = Some(future.await);
      } else {
        // 装箱并固定Future,满足Unpin要求
        pending[i] = Some(Box::pin(future));
      }
    }
    
    // 遍历可变引用执行pending中的Future
    for (i, maybe_future) in pending.iter_mut().enumerate() {
      if let Some(future) = maybe_future.take() {
        responses[i] = Some(future.await);
      }
    }
    
    // 转换为最终结果Vec
    responses.into_iter().map(|x| x.unwrap()).collect()
  }
}

方法2:给Trait的返回Future加Unpin约束

如果场景允许,可以修改Trait定义,强制返回的Future实现Unpin:

pub trait MakesWidgets {
  // 直接约束返回的Future为Unpin
  fn make_widget(&self) -> impl Future<Output = Result<Widget, String>> + Unpin;
}

注意:这种方式会限制Trait的实现,因为不是所有async函数返回的Future都自动实现Unpin,仅推荐在可控场景下使用。

额外修复点

原代码还有两处小问题需要修正:

  1. 遍历pending时需使用iter_mut()而非iter(),才能获取可变引用操作Future
  2. responses.map(...)错误,Vec没有map方法,需先通过into_iter()转为迭代器再用collect()生成结果

内容的提问来源于stack exchange,提问作者tacos_tacos_tacos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:20:28