Skip to content

Commit 735fa69

Browse files
yoshuawuytsStjepan Glavina
authored and
Stjepan Glavina
committed
Replace select!/try_select! with Future::{race,try_race} (#405)
* init Future::select Signed-off-by: Yoshua Wuyts <[email protected]> * implement Future::select Signed-off-by: Yoshua Wuyts <[email protected]> * try_select Signed-off-by: Yoshua Wuyts <[email protected]> * fixes Signed-off-by: Yoshua Wuyts <[email protected]> * works Signed-off-by: Yoshua Wuyts <[email protected]> * pass clippy Signed-off-by: Yoshua Wuyts <[email protected]> * please clippy Signed-off-by: Yoshua Wuyts <[email protected]> * implement feedback from stjepan Signed-off-by: Yoshua Wuyts <[email protected]> * rename select to race Signed-off-by: Yoshua Wuyts <[email protected]> * fmt Signed-off-by: Yoshua Wuyts <[email protected]>
1 parent 3a2e6d5 commit 735fa69

File tree

5 files changed

+234
-17
lines changed

5 files changed

+234
-17
lines changed

src/future/future.rs renamed to src/future/future/mod.rs

+92
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
cfg_unstable! {
22
mod delay;
3+
mod race;
4+
mod try_race;
35

46
use std::time::Duration;
57

68
use delay::DelayFuture;
9+
use race::Race;
10+
use try_race::TryRace;
711
}
812

913
extension_trait! {
@@ -129,6 +133,94 @@ extension_trait! {
129133
{
130134
DelayFuture::new(self, dur)
131135
}
136+
137+
#[doc = r#"
138+
Waits for one of two similarly-typed futures to complete.
139+
140+
Awaits multiple futures simultaneously, returning the output of the
141+
first future that completes.
142+
143+
This function will return a new future which awaits for either one of both
144+
futures to complete. If multiple futures are completed at the same time,
145+
resolution will occur in the order that they have been passed.
146+
147+
Note that this macro consumes all futures passed, and once a future is
148+
completed, all other futures are dropped.
149+
150+
This macro is only usable inside of async functions, closures, and blocks.
151+
152+
# Examples
153+
154+
```
155+
# async_std::task::block_on(async {
156+
use async_std::prelude::*;
157+
use async_std::future;
158+
159+
let a = future::pending();
160+
let b = future::ready(1u8);
161+
let c = future::ready(2u8);
162+
163+
let f = a.race(b).race(c);
164+
assert_eq!(f.await, 1u8);
165+
# });
166+
```
167+
"#]
168+
#[cfg(any(feature = "unstable", feature = "docs"))]
169+
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
170+
fn race<F>(
171+
self,
172+
other: F
173+
) -> impl Future<Output = <Self as std::future::Future>::Output> [Race<Self, F>]
174+
where
175+
Self: std::future::Future + Sized,
176+
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
177+
{
178+
Race::new(self, other)
179+
}
180+
181+
#[doc = r#"
182+
Waits for one of two similarly-typed fallible futures to complete.
183+
184+
Awaits multiple futures simultaneously, returning all results once complete.
185+
186+
`try_race` is similar to [`race`], but keeps going if a future
187+
resolved to an error until all futures have been resolved. In which case
188+
an error is returned.
189+
190+
The ordering of which value is yielded when two futures resolve
191+
simultaneously is intentionally left unspecified.
192+
193+
# Examples
194+
195+
```
196+
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
197+
#
198+
use async_std::prelude::*;
199+
use async_std::future;
200+
use std::io::{Error, ErrorKind};
201+
202+
let a = future::pending::<Result<_, Error>>();
203+
let b = future::ready(Err(Error::from(ErrorKind::Other)));
204+
let c = future::ready(Ok(1u8));
205+
206+
let f = a.try_race(b).try_race(c);
207+
assert_eq!(f.await?, 1u8);
208+
#
209+
# Ok(()) }) }
210+
```
211+
"#]
212+
#[cfg(any(feature = "unstable", feature = "docs"))]
213+
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
214+
fn try_race<F: std::future::Future, T, E>(
215+
self,
216+
other: F
217+
) -> impl Future<Output = <Self as std::future::Future>::Output> [TryRace<Self, F>]
218+
where
219+
Self: std::future::Future<Output = Result<T, E>> + Sized,
220+
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
221+
{
222+
TryRace::new(self, other)
223+
}
132224
}
133225

134226
impl<F: Future + Unpin + ?Sized> Future for Box<F> {

src/future/future/race.rs

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use std::pin::Pin;
2+
3+
use async_macros::MaybeDone;
4+
use pin_project_lite::pin_project;
5+
6+
use crate::task::{Context, Poll};
7+
use std::future::Future;
8+
9+
pin_project! {
10+
#[allow(missing_docs)]
11+
#[allow(missing_debug_implementations)]
12+
pub struct Race<L, R>
13+
where
14+
L: Future,
15+
R: Future<Output = L::Output>
16+
{
17+
#[pin] left: MaybeDone<L>,
18+
#[pin] right: MaybeDone<R>,
19+
}
20+
}
21+
22+
impl<L, R> Race<L, R>
23+
where
24+
L: Future,
25+
R: Future<Output = L::Output>,
26+
{
27+
pub(crate) fn new(left: L, right: R) -> Self {
28+
Self {
29+
left: MaybeDone::new(left),
30+
right: MaybeDone::new(right),
31+
}
32+
}
33+
}
34+
35+
impl<L, R> Future for Race<L, R>
36+
where
37+
L: Future,
38+
R: Future<Output = L::Output>,
39+
{
40+
type Output = L::Output;
41+
42+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
43+
let this = self.project();
44+
45+
let mut left = this.left;
46+
if Future::poll(Pin::new(&mut left), cx).is_ready() {
47+
return Poll::Ready(left.take().unwrap());
48+
}
49+
50+
let mut right = this.right;
51+
if Future::poll(Pin::new(&mut right), cx).is_ready() {
52+
return Poll::Ready(right.take().unwrap());
53+
}
54+
55+
Poll::Pending
56+
}
57+
}

src/future/future/try_race.rs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use std::pin::Pin;
2+
3+
use async_macros::MaybeDone;
4+
use pin_project_lite::pin_project;
5+
6+
use crate::task::{Context, Poll};
7+
use std::future::Future;
8+
9+
pin_project! {
10+
#[allow(missing_docs)]
11+
#[allow(missing_debug_implementations)]
12+
pub struct TryRace<L, R>
13+
where
14+
L: Future,
15+
R: Future<Output = L::Output>
16+
{
17+
#[pin] left: MaybeDone<L>,
18+
#[pin] right: MaybeDone<R>,
19+
}
20+
}
21+
22+
impl<L, R> TryRace<L, R>
23+
where
24+
L: Future,
25+
R: Future<Output = L::Output>,
26+
{
27+
pub(crate) fn new(left: L, right: R) -> Self {
28+
Self {
29+
left: MaybeDone::new(left),
30+
right: MaybeDone::new(right),
31+
}
32+
}
33+
}
34+
35+
impl<L, R, T, E> Future for TryRace<L, R>
36+
where
37+
L: Future<Output = Result<T, E>>,
38+
R: Future<Output = L::Output>,
39+
{
40+
type Output = L::Output;
41+
42+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
43+
let this = self.project();
44+
let mut left_errored = false;
45+
46+
// Check if the left future is ready & successful. Continue if not.
47+
let mut left = this.left;
48+
if Future::poll(Pin::new(&mut left), cx).is_ready() {
49+
if left.as_ref().output().unwrap().is_ok() {
50+
return Poll::Ready(left.take().unwrap());
51+
} else {
52+
left_errored = true;
53+
}
54+
}
55+
56+
// Check if the right future is ready & successful. Return err if left
57+
// future also resolved to err. Continue if not.
58+
let mut right = this.right;
59+
let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
60+
if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) {
61+
return Poll::Ready(right.take().unwrap());
62+
}
63+
64+
Poll::Pending
65+
}
66+
}

src/future/mod.rs

+18-16
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
//!
55
//! Often it's desireable to await multiple futures as if it was a single
66
//! future. The `join` family of operations converts multiple futures into a
7-
//! single future that returns all of their outputs. The `select` family of
7+
//! single future that returns all of their outputs. The `race` family of
88
//! operations converts multiple future into a single future that returns the
99
//! first output.
1010
//!
1111
//! For operating on futures the following macros can be used:
1212
//!
13-
//! | Name | Return signature | When does it return? |
14-
//! | --- | --- | --- |
15-
//! | `future::join` | `(T1, T2)` | Wait for all to complete
16-
//! | `future::select` | `T` | Return on first value
13+
//! | Name | Return signature | When does it return? |
14+
//! | --- | --- | --- |
15+
//! | [`future::join!`] | `(T1, T2)` | Wait for all to complete
16+
//! | [`Future::race`] | `T` | Return on first value
1717
//!
1818
//! ## Fallible Futures Concurrency
1919
//!
@@ -25,21 +25,26 @@
2525
//! futures are dropped and an error is returned. This is referred to as
2626
//! "short-circuiting".
2727
//!
28-
//! In the case of `try_select`, instead of returning the first future that
28+
//! In the case of `try_race`, instead of returning the first future that
2929
//! completes it returns the first future that _successfully_ completes. This
30-
//! means `try_select` will keep going until any one of the futures returns
30+
//! means `try_race` will keep going until any one of the futures returns
3131
//! `Ok`, or _all_ futures have returned `Err`.
3232
//!
3333
//! However sometimes it can be useful to use the base variants of the macros
3434
//! even on futures that return `Result`. Here is an overview of operations that
3535
//! work on `Result`, and their respective semantics:
3636
//!
37-
//! | Name | Return signature | When does it return? |
38-
//! | --- | --- | --- |
39-
//! | `future::join` | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
40-
//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
41-
//! | `future::select` | `Result<T, E>` | Return on first value
42-
//! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err
37+
//! | Name | Return signature | When does it return? |
38+
//! | --- | --- | --- |
39+
//! | [`future::join!`] | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
40+
//! | [`future::try_join!`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
41+
//! | [`Future::race`] | `Result<T, E>` | Return on first value
42+
//! | [`Future::try_race`] | `Result<T, E>` | Return on first `Ok`, reject on last Err
43+
//!
44+
//! [`future::join!`]: macro.join.html
45+
//! [`future::try_join!`]: macro.try_join.html
46+
//! [`Future::race`]: trait.Future.html#method.race
47+
//! [`Future::try_race`]: trait.Future.html#method.try_race
4348
4449
#[doc(inline)]
4550
pub use async_macros::{join, try_join};
@@ -57,9 +62,6 @@ mod ready;
5762
mod timeout;
5863

5964
cfg_unstable! {
60-
#[doc(inline)]
61-
pub use async_macros::{select, try_select};
62-
6365
pub use into_future::IntoFuture;
6466
mod into_future;
6567
}

src/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ macro_rules! extension_trait {
190190
};
191191

192192
// Parse the return type in an extension method.
193-
(@doc ($($head:tt)*) -> impl Future<Output = $out:ty> [$f:ty] $($tail:tt)*) => {
193+
(@doc ($($head:tt)*) -> impl Future<Output = $out:ty> $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => {
194194
extension_trait!(@doc ($($head)* -> owned::ImplFuture<$out>) $($tail)*);
195195
};
196196
(@ext ($($head:tt)*) -> impl Future<Output = $out:ty> $(+ $lt:lifetime)? [$f:ty] $($tail:tt)*) => {

0 commit comments

Comments
 (0)