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

no_std环境下如何在select循环中复用同一Future?

问题背景

我有两个异步函数get_message和get_event,希望在消息到达或事件发生时执行对应操作,并持续进行无限循环。简化后的代码结构如下:

use futures::{future::select, future::Either, pin_mut};

impl MsgReceiver {
    async fn get_message(&mut self) -> Message { /* ... */ }
}

impl EventListener {
    async fn get_event(&mut self) -> Event { /* ... */ }
}

async fn eternal_task(receiver: MsgReceiver, listener: EventListener) -> ! {
    let get_msg_fut = receiver.get_message();
    pin_mut!(get_msg_fut);
    loop {
        let get_event_fut = listener.get_event();

        pin_mut!(get_event_fut);

        match select(get_event_fut, get_msg_fut).await {
            Either::Left((ev, r_get_msg_fut)) => {
                /* react to the event */

                // r_get_msg_fut is not done, how to reuse it in the next iteration?
            }
            Either::Right((msg, r_get_event_fut)) => {
                /* react to the message */

                // it's fine to drop get_event_fut here

                // the following line causes a double-mut-borrow error on receiver,
                // despite receiver isn't borrowed anymore (the old future is completed and dropped)
                let new_future = receiver.get_message();
            }
        };
    }
}

核心问题

  1. 当事件先触发时,如何让Rust在下一轮循环中复用未完成的get_message Future?
  2. 当消息先触发时,如何构造新的Future而避免双重可变借用错误?
  3. 解决问题2后,如何将新Future放入同一固定内存位置并用于下一轮循环?

解决方案

针对问题1:复用未完成的Future

select返回的r_get_msg_fut就是未完成的消息Future,只需将它重新赋值回存储消息任务的容器即可。关键是要让容器支持更新操作,同时满足Future的Pin要求。

针对问题2:避免双重可变借用

原代码中pin_mut!固定的future会让编译器认为receiver的可变借用一直被持有,直到future被销毁。改用堆上分配的boxed future(Pin<Box<dyn Future<Output = Message>>>),可以在旧future完成后先销毁它,再创建新future,此时receiver的借用会被正确释放。

针对问题3:固定内存位置复用Future

Pin<Box<dyn Future<...>>>类型的future存储在堆上,内存位置固定,同时支持重新赋值。每次消息处理完成后,直接用新的boxed future替换旧的即可,无需改变内存地址,满足Pin的要求。

修改后的完整代码

use futures::{future::select, future::Either, FutureExt};
use std::pin::Pin;

impl MsgReceiver {
    async fn get_message(&mut self) -> Message { /* ... */ }
}

impl EventListener {
    async fn get_event(&mut self) -> Event { /* ... */ }
}

async fn eternal_task(mut receiver: MsgReceiver, mut listener: EventListener) -> ! {
    // 用boxed future存储消息任务,保证内存位置固定且可替换
    let mut get_msg_fut: Pin<Box<dyn Future<Output = Message>>> = Box::pin(receiver.get_message());
    
    loop {
        let get_event_fut = listener.get_event();
        
        match select(get_event_fut.boxed(), get_msg_fut).await {
            Either::Left((ev, remaining_msg_fut)) => {
                // 处理事件逻辑
                handle_event(ev);
                
                // 将未完成的消息任务放回容器,用于下一轮循环
                get_msg_fut = remaining_msg_fut;
            }
            Either::Right((msg, _remaining_event_fut)) => {
                // 处理消息逻辑
                handle_message(msg);
                
                // 旧消息任务已完成,创建新任务并替换容器内容
                get_msg_fut = Box::pin(receiver.get_message());
            }
        };
    }
}

// 示例处理函数(根据实际需求实现)
fn handle_event(_ev: Event) {}
fn handle_message(_msg: Message) {}

关键说明

  • Box::pin将future装箱并固定,确保其内存地址不会变动,符合Pin的安全要求。
  • 事件任务每次循环重新创建,因为事件触发后无需复用未完成的任务(匹配你的需求描述)。
  • 消息任务完成时,旧的boxed future会被自动销毁,receiver的可变借用随之释放,此时创建新future不会触发双重借用错误。
  • 事件触发后,直接将返回的剩余消息future赋值回get_msg_fut,即可实现未完成任务的复用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 16:05:32