Skip to content

Commit bd9aaf5

Browse files
committed
try_select
Signed-off-by: Yoshua Wuyts <[email protected]>
1 parent 5e27297 commit bd9aaf5

File tree

3 files changed

+109
-6
lines changed

3 files changed

+109
-6
lines changed

src/future/future/mod.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
mod select;
2+
mod try_select;
23

34
use select::Select;
5+
use try_select::TrySelect;
46

57
extension_trait! {
68
use std::pin::Pin;
@@ -104,7 +106,7 @@ extension_trait! {
104106

105107
pub trait FutureExt: std::future::Future {
106108
#[doc = r#"
107-
Waits for either one of several similarly-typed futures to complete.
109+
Waits for one of two similarly-typed futures to complete.
108110
109111
Awaits multiple futures simultaneously, returning all results once complete.
110112
@@ -120,7 +122,7 @@ extension_trait! {
120122
# Examples
121123
122124
```
123-
# futures::executor::block_on(async {
125+
# async_std::task::block_on(async {
124126
use async_std::prelude::*;
125127
use async_std::future;
126128
@@ -133,12 +135,51 @@ extension_trait! {
133135
# });
134136
```
135137
"#]
136-
fn select<F>(self, other: F) -> Select<Self, F>
138+
fn select<F>(self, other: F) ->
139+
impl Future<Output = Self::Output> [Select<Self, F>]
140+
where
141+
Self: Sized + Future,
142+
F: std::future::Future<Output = <Self as Future>::Output>,
143+
{
144+
Select::new(self, other)
145+
}
146+
147+
#[doc = r#"
148+
Waits for one of two similarly-typed fallible futures to complete.
149+
150+
Awaits multiple futures simultaneously, returning all results once complete.
151+
152+
`try_select` is similar to [`select`], but keeps going if a future
153+
resolved to an error until all futures have been resolved. In which case
154+
the error of the `other` future will be returned.
155+
156+
# Examples
157+
158+
```
159+
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
160+
#
161+
use async_std::prelude::*;
162+
use async_std::future;
163+
use std::io::{Error, ErrorKind};
164+
165+
let a = future::pending::<Result<_, Error>>();
166+
let b = future::ready(Err(Error::from(ErrorKind::Other)));
167+
let c = future::ready(Ok(1u8));
168+
169+
let f = a.try_select(b).try_select(c);
170+
assert_eq!(f.await?, 1u8);
171+
#
172+
# Ok(()) }) }
173+
```
174+
"#]
175+
fn try_select<F, T, E>(self, other: F) ->
176+
impl Future<Output = Self::Output> [TrySelect<Self, F>]
137177
where
138178
Self: Sized,
179+
Self: Future<Output = Result<T, E>>,
139180
F: Future<Output = Self::Output>,
140181
{
141-
Select::new(self, other)
182+
TrySelect::new(self, other)
142183
}
143184
}
144185

src/future/future/select.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::pin::Pin;
33
use pin_project_lite::pin_project;
44
use async_macros::MaybeDone;
55

6-
use crate::future::Future;
7-
// use crate::future::MaybeDone;
6+
use std::future::Future;
87
use crate::task::{Context, Poll};
98

109
pin_project! {

src/future/future/try_select.rs

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

0 commit comments

Comments
 (0)