Description
The select
macro would be far more useful if it was usable inside of loops, like this:
pin_mut!(future_1, future_2, my_stream);
let mut state = ...;
loop {
let x = select! {
future_1 => { ...transform state... }
future_2 => { ...transform state... }
next(my_stream) as elem => { ... transform state... }
done => { ... }
};
...some more code doing something with x and state...
}
However, the Stream
and Future
traits require that they are not polled after completion, and they provide no way to check if completion has already occurred. It's possible to track this extra bit of state in a boolean or something as part of the macro, but then the looping functionality has to be built into the macro, meaning that (1) there would have to be separate select!
and select_loop!
macros and (2) any shared code or controlflow in the "always" branch would have to be duplicated across branches:
pin_mut!(future_1, future_2, my_stream);
let mut state = ...;
select_loop! {
future_1 => { let x = ...transform state...; ...some more code doing something with x and state... }
future_2 => { let x = ...transform state...; ...some more code doing something with x and state... }
next(my_stream) as elem => { let x = ...transform state...; ...some more code doing something with x and state... }
done => { let x = ...transform state...; ...some more code doing something with x and state... }
}
Another option would be to add FusedFuture
and FusedStream
with has_completed()
methods, implement FusedFuture
and FusedStream
for many of the combinators which support them (in particular the fuse()
combinator). This is likely more ergonomic for users familiar with how the system works, but could be confusing for new users who now have one more thing to understand (fusing, pinning, etc...).
Yet another option would be to require that all futures track this bit of state and add a has_completed
method to the Future and Stream traits. This requires less involvement from end-users, but again increases the already-high ergonomic burden on manual-futures implementors.
Personally, I lean towards the FusedFuture
/FusedStream
approach as it adds minimal complexity for users who don't care about the select!
combinator while still retaining the manual control-flow flexibility of select!
+ loop
rather than select_loop!
. However, I'm by no means attached to this idea, and I recognize that it pushes another point of complexity onto end users, which is disappointing. I'd like to hear what y'all think.
cc @seanmonstar @carllerche @withoutboats @aturon @Nemo157 @MajorBreakfast @cavedweller @tkilbourn @smklein