Skip to content

Commit 54ce543

Browse files
committed
Add select_biased, document how it differs from select
1 parent baeca1b commit 54ce543

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

futures-util/src/future/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ pub use self::join_all::{join_all, JoinAll};
8585
mod select;
8686
pub use self::select::{select, Select};
8787

88+
mod select_biased;
89+
pub use self::select_biased::{select_biased, SelectBiased};
90+
8891
#[cfg(feature = "alloc")]
8992
mod select_all;
9093
#[cfg(feature = "alloc")]

futures-util/src/future/select.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ impl<A: Unpin, B: Unpin> Unpin for Select<A, B> {}
2222
/// Note that this function consumes the receiving futures and returns a
2323
/// wrapped version of them.
2424
///
25-
/// If both futures are ready when this is polled, the winner will be pseudo-randomly
26-
/// selected.
25+
/// **If both futures are ready when this is polled, the winner will be pseudo-randomly
26+
/// selected, unless this crate is compiled without the `std` feature. Without `std`,
27+
/// the first argument will always be selected.**
2728
///
2829
/// Also note that if both this and the second future have the same
2930
/// output type you can use the `Either::factor_first` method to
+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
use super::assert_future;
2+
use crate::future::{Either, FutureExt};
3+
use core::pin::Pin;
4+
use futures_core::future::{FusedFuture, Future};
5+
use futures_core::task::{Context, Poll};
6+
7+
/// Future for the [`select_biased()`] function.
8+
#[must_use = "futures do nothing unless you `.await` or poll them"]
9+
#[derive(Debug)]
10+
pub struct SelectBiased<A, B> {
11+
inner: Option<(A, B)>,
12+
}
13+
14+
impl<A: Unpin, B: Unpin> Unpin for SelectBiased<A, B> {}
15+
16+
/// Waits for either one of two differently-typed futures to complete, giving preferential treatment to the first argument.
17+
///
18+
/// This function will return a new future which awaits for either one of both
19+
/// futures to complete. The returned future will finish with both the value
20+
/// resolved and a future representing the completion of the other work.
21+
///
22+
/// Note that this function consumes the receiving futures and returns a
23+
/// wrapped version of them.
24+
///
25+
/// **If both futures are ready when this is polled, the first argument will always be selected.**
26+
///
27+
/// Also note that if both this and the second future have the same
28+
/// output type you can use the `Either::factor_first` method to
29+
/// conveniently extract out the value at the end.
30+
///
31+
/// # Examples
32+
///
33+
/// A simple example
34+
///
35+
/// ```
36+
/// # futures::executor::block_on(async {
37+
/// use futures::{
38+
/// pin_mut,
39+
/// future::Either,
40+
/// future::self,
41+
/// };
42+
///
43+
/// // These two futures have different types even though their outputs have the same type.
44+
/// let future1 = async {
45+
/// future::pending::<()>().await; // will never finish
46+
/// 1
47+
/// };
48+
/// let future2 = async {
49+
/// future::ready(2).await
50+
/// };
51+
///
52+
/// // 'select_biased' requires Future + Unpin bounds
53+
/// pin_mut!(future1);
54+
/// pin_mut!(future2);
55+
///
56+
/// let value = match future::select_biased(future1, future2).await {
57+
/// Either::Left((value1, _)) => value1, // `value1` is resolved from `future1`
58+
/// // `_` represents `future2`
59+
/// Either::Right((value2, _)) => value2, // `value2` is resolved from `future2`
60+
/// // `_` represents `future1`
61+
/// };
62+
///
63+
/// assert!(value == 2);
64+
/// # });
65+
/// ```
66+
///
67+
/// A more complex example
68+
///
69+
/// ```
70+
/// use futures::future::{self, Either, Future, FutureExt};
71+
///
72+
/// // A poor-man's join implemented on top of select_biased
73+
///
74+
/// fn join<A, B>(a: A, b: B) -> impl Future<Output=(A::Output, B::Output)>
75+
/// where A: Future + Unpin,
76+
/// B: Future + Unpin,
77+
/// {
78+
/// future::select_biased(a, b).then(|either| {
79+
/// match either {
80+
/// Either::Left((x, b)) => b.map(move |y| (x, y)).left_future(),
81+
/// Either::Right((y, a)) => a.map(move |x| (x, y)).right_future(),
82+
/// }
83+
/// })
84+
/// }
85+
/// ```
86+
pub fn select_biased<A, B>(future1: A, future2: B) -> SelectBiased<A, B>
87+
where
88+
A: Future + Unpin,
89+
B: Future + Unpin,
90+
{
91+
assert_future::<Either<(A::Output, B), (B::Output, A)>, _>(SelectBiased {
92+
inner: Some((future1, future2)),
93+
})
94+
}
95+
96+
impl<A, B> Future for SelectBiased<A, B>
97+
where
98+
A: Future + Unpin,
99+
B: Future + Unpin,
100+
{
101+
type Output = Either<(A::Output, B), (B::Output, A)>;
102+
103+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
104+
/// When compiled with `-C opt-level=z`, this function will help the compiler eliminate the `None` branch, where
105+
/// `Option::unwrap` does not.
106+
#[inline(always)]
107+
fn unwrap_option<T>(value: Option<T>) -> T {
108+
match value {
109+
None => unreachable!(),
110+
Some(value) => value,
111+
}
112+
}
113+
114+
let (a, b) = self.inner.as_mut().expect("cannot poll Select twice");
115+
116+
macro_rules! poll_wrap {
117+
($to_poll:expr, $unpolled:expr, $wrap:expr) => {
118+
if let Poll::Ready(val) = $to_poll.poll_unpin(cx) {
119+
return Poll::Ready($wrap((val, $unpolled)));
120+
}
121+
};
122+
}
123+
124+
poll_wrap!(a, unwrap_option(self.inner.take()).1, Either::Left);
125+
poll_wrap!(b, unwrap_option(self.inner.take()).0, Either::Right);
126+
Poll::Pending
127+
}
128+
}
129+
130+
impl<A, B> FusedFuture for SelectBiased<A, B>
131+
where
132+
A: Future + Unpin,
133+
B: Future + Unpin,
134+
{
135+
fn is_terminated(&self) -> bool {
136+
self.inner.is_none()
137+
}
138+
}

0 commit comments

Comments
 (0)