diff --git a/futures-util/src/future/mod.rs b/futures-util/src/future/mod.rs
index 97a8c8ff7..419d01c3b 100644
--- a/futures-util/src/future/mod.rs
+++ b/futures-util/src/future/mod.rs
@@ -82,6 +82,9 @@ mod join_all;
#[cfg(feature = "alloc")]
pub use self::join_all::{join_all, JoinAll};
+mod select_strategy;
+pub use select_strategy::{Biased, Fair, IsBiased};
+
mod select;
pub use self::select::{select, select_biased, Select};
diff --git a/futures-util/src/future/select.rs b/futures-util/src/future/select.rs
index 7d5eba436..cbce54c3a 100644
--- a/futures-util/src/future/select.rs
+++ b/futures-util/src/future/select.rs
@@ -1,5 +1,6 @@
-use super::assert_future;
+use super::{assert_future, Biased, Fair, IsBiased};
use crate::future::{Either, FutureExt};
+use core::marker::PhantomData;
use core::pin::Pin;
use futures_core::future::{FusedFuture, Future};
use futures_core::task::{Context, Poll};
@@ -7,12 +8,12 @@ use futures_core::task::{Context, Poll};
/// Future for the [`select()`] function.
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
-pub struct Select {
+pub struct Select {
inner: Option<(A, B)>,
- _biased: bool,
+ _phantom: PhantomData,
}
-impl Unpin for Select {}
+impl Unpin for Select {}
/// Waits for either one of two differently-typed futures to complete.
///
@@ -86,14 +87,14 @@ impl Unpin for Select {}
/// })
/// }
/// ```
-pub fn select(future1: A, future2: B) -> Select
+pub fn select(future1: A, future2: B) -> Select
where
A: Future + Unpin,
B: Future + Unpin,
{
assert_future::, _>(Select {
inner: Some((future1, future2)),
- _biased: false,
+ _phantom: PhantomData,
})
}
@@ -167,21 +168,22 @@ where
/// })
/// }
/// ```
-pub fn select_biased(future1: A, future2: B) -> Select
+pub fn select_biased(future1: A, future2: B) -> Select
where
A: Future + Unpin,
B: Future + Unpin,
{
assert_future::, _>(Select {
inner: Some((future1, future2)),
- _biased: true,
+ _phantom: PhantomData,
})
}
-impl Future for Select
+impl Future for Select
where
A: Future + Unpin,
B: Future + Unpin,
+ BIASED: IsBiased,
{
type Output = Either<(A::Output, B), (B::Output, A)>;
@@ -195,7 +197,6 @@ where
Some(value) => value,
}
}
- let _biased = self._biased;
let (a, b) = self.inner.as_mut().expect("cannot poll Select twice");
@@ -208,7 +209,7 @@ where
}
#[cfg(feature = "std")]
- if _biased || crate::gen_index(2) == 0 {
+ if BIASED::IS_BIASED || crate::gen_index(2) == 0 {
poll_wrap!(a, unwrap_option(self.inner.take()).1, Either::Left);
poll_wrap!(b, unwrap_option(self.inner.take()).0, Either::Right);
} else {
@@ -225,10 +226,11 @@ where
}
}
-impl FusedFuture for Select
+impl FusedFuture for Select
where
A: Future + Unpin,
B: Future + Unpin,
+ BIASED: IsBiased,
{
fn is_terminated(&self) -> bool {
self.inner.is_none()
diff --git a/futures-util/src/future/select_ok.rs b/futures-util/src/future/select_ok.rs
index 1aadd9c5e..848cd52b8 100644
--- a/futures-util/src/future/select_ok.rs
+++ b/futures-util/src/future/select_ok.rs
@@ -1,7 +1,9 @@
use super::assert_future;
+use super::{Biased, Fair, IsBiased};
use crate::future::TryFutureExt;
use alloc::vec::Vec;
use core::iter::FromIterator;
+use core::marker::PhantomData;
use core::mem;
use core::pin::Pin;
use futures_core::future::{Future, TryFuture};
@@ -10,12 +12,12 @@ use futures_core::task::{Context, Poll};
/// Future for the [`select_ok`] function.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
-pub struct SelectOk {
+pub struct SelectOk {
inner: Vec,
- _biased: bool,
+ _phantom: PhantomData,
}
-impl Unpin for SelectOk {}
+impl Unpin for SelectOk {}
/// Creates a new future which will select the first successful future over a list of futures.
///
@@ -41,12 +43,12 @@ impl Unpin for SelectOk {}
/// # Panics
///
/// This function will panic if the iterator specified contains no items.
-pub fn select_ok(iter: I) -> SelectOk
+pub fn select_ok(iter: I) -> SelectOk
where
I: IntoIterator,
I::Item: TryFuture + Unpin,
{
- let ret = SelectOk { inner: iter.into_iter().collect(), _biased: false };
+ let ret = SelectOk { inner: iter.into_iter().collect(), _phantom: PhantomData };
assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty");
assert_future::<
Result<(::Ok, Vec), ::Error>,
@@ -70,12 +72,12 @@ where
/// # Panics
///
/// This function will panic if the iterator specified contains no items.
-pub fn select_ok_biased(iter: I) -> SelectOk
+pub fn select_ok_biased(iter: I) -> SelectOk
where
I: IntoIterator,
I::Item: TryFuture + Unpin,
{
- let ret = SelectOk { inner: iter.into_iter().collect(), _biased: true };
+ let ret = SelectOk { inner: iter.into_iter().collect(), _phantom: PhantomData };
assert!(!ret.inner.is_empty(), "iterator provided to select_ok was empty");
assert_future::<
Result<(::Ok, Vec), ::Error>,
@@ -83,14 +85,14 @@ where
>(ret)
}
-impl Future for SelectOk {
+impl Future for SelectOk {
type Output = Result<(Fut::Ok, Vec), Fut::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
- let Self { inner, _biased } = &mut *self;
+ let Self { inner, _phantom } = &mut *self;
#[cfg(feature = "std")]
{
- if !*_biased {
+ if !BIASED::IS_BIASED {
crate::shuffle(inner);
}
}
diff --git a/futures-util/src/future/select_strategy.rs b/futures-util/src/future/select_strategy.rs
new file mode 100644
index 000000000..4d82e1328
--- /dev/null
+++ b/futures-util/src/future/select_strategy.rs
@@ -0,0 +1,25 @@
+/// When used with a select future, this will make the future biased.
+/// When multiple futures are ready, the winner will be the first one
+/// specified.
+#[derive(Debug)]
+pub struct Biased;
+
+/// When used with a select future, this will make the future fair.
+/// When multiple futures are ready, the winner will be pseudo-randomly
+/// selected. This is the default behavior.
+#[derive(Debug)]
+pub struct Fair;
+
+/// Reports whether the type is an instance of [`Biased`] or not.
+pub trait IsBiased {
+ /// Contains the answer to our question: is this biased?
+ const IS_BIASED: bool;
+}
+
+impl IsBiased for Biased {
+ const IS_BIASED: bool = true;
+}
+
+impl IsBiased for Fair {
+ const IS_BIASED: bool = false;
+}
diff --git a/futures-util/src/future/try_select.rs b/futures-util/src/future/try_select.rs
index 8927865d2..fbc3e5c6a 100644
--- a/futures-util/src/future/try_select.rs
+++ b/futures-util/src/future/try_select.rs
@@ -1,4 +1,6 @@
+use super::{Biased, Fair, IsBiased};
use crate::future::{Either, TryFutureExt};
+use core::marker::PhantomData;
use core::pin::Pin;
use futures_core::future::{Future, TryFuture};
use futures_core::task::{Context, Poll};
@@ -6,12 +8,12 @@ use futures_core::task::{Context, Poll};
/// Future for the [`try_select()`] function.
#[must_use = "futures do nothing unless you `.await` or poll them"]
#[derive(Debug)]
-pub struct TrySelect {
+pub struct TrySelect {
inner: Option<(A, B)>,
- _biased: bool,
+ _phantom: PhantomData,
}
-impl Unpin for TrySelect {}
+impl Unpin for TrySelect {}
type EitherOk = Either<(::Ok, B), (::Ok, A)>;
type EitherErr = Either<(::Error, B), (::Error, A)>;
@@ -55,14 +57,14 @@ type EitherErr = Either<(::Error, B), (::E
/// })
/// }
/// ```
-pub fn try_select(future1: A, future2: B) -> TrySelect
+pub fn try_select(future1: A, future2: B) -> TrySelect
where
A: TryFuture + Unpin,
B: TryFuture + Unpin,
{
super::assert_future::, EitherErr>, _>(TrySelect {
inner: Some((future1, future2)),
- _biased: false,
+ _phantom: PhantomData,
})
}
@@ -103,21 +105,22 @@ where
/// })
/// }
/// ```
-pub fn try_select_biased(future1: A, future2: B) -> TrySelect
+pub fn try_select_biased(future1: A, future2: B) -> TrySelect
where
A: TryFuture + Unpin,
B: TryFuture + Unpin,
{
super::assert_future::, EitherErr>, _>(TrySelect {
inner: Some((future1, future2)),
- _biased: true,
+ _phantom: PhantomData,
})
}
-impl Future for TrySelect
+impl Future for TrySelect
where
A: TryFuture,
B: TryFuture,
+ BIASED: IsBiased,
{
type Output = Result, EitherErr>;
@@ -142,7 +145,7 @@ where
#[cfg(feature = "std")]
{
- if self._biased || crate::gen_index(2) == 0 {
+ if BIASED::IS_BIASED || crate::gen_index(2) == 0 {
poll_wrap!(a, b, Either::Left, Either::Right)
} else {
poll_wrap!(b, a, Either::Right, Either::Left)