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(); } }; } }
核心问题
- 当事件先触发时,如何让Rust在下一轮循环中复用未完成的
get_messageFuture? - 当消息先触发时,如何构造新的Future而避免双重可变借用错误?
- 解决问题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
相关产品推荐
相关产品推荐

