I have two async functions: get_message and get_event. I'd like to perform an action whenever a message arrives or an event comes and do that forever in an infinite loop.
The simplified setup looks like this:
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();
}
};
}
}
I have three major questions here:
- When an event comes first, how to tell rust that I want to reuse the incomplete get_message future on the next loop iteration?
- When a message comes first, how to construct a new future without a borrow error?
- When (2) is solved, how to put the new future into the same pinned memory location and use it on the next loop iteration?
I think this is tricky to get right, even with
unsafe
which would probably be needed to accomplish this. Persisting and reusing the same variables isn't too hard, its actually #2 that's the hardest (at least with the current borrow checker).I found a solution that totally circumvents the problem by using the async-stream crate to provide an intermediary:
It uses the
stream!
macro to generate a type that continuously calls and yields values from.get_message()
and.get_event()
. It then usesfutures::stream::select
andEither
to combine them. And then its just a matter of looping over the results. It works in#![no_std]
.