@@ -23,17 +23,10 @@ impl<F: FusedFuture + TryFuture> Future for FirstOk<F> {
23
23
#[ inline]
24
24
fn poll ( self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
25
25
// Basic logic diagram:
26
- // - If all existing futures are terminated, return Pending. This means
27
- // someone polled after this future returned ready, or that this
28
- // future will never return ready because a future spuriously
29
- // terminated itself.
30
- // - If a future returns Ok, clear the vector (this is safe because
31
- // vec drops in place), then return that value. We clear the vector
32
- // so that our FusedFuture impl, which checks `are all futures
33
- // terminated`, works correctly.
26
+ // - If all existing futures are terminated, return Pending.
27
+ // - If a future returns Ok, return that value.
34
28
// - If all existing futures BECOME terminated while polling them, and
35
- // an error was returned, return the final error; otherwise return
36
- // pending.
29
+ // an error was returned, return the final error.
37
30
38
31
/// Helper enum to track our state as we poll each future
39
32
enum State < E > {
@@ -64,19 +57,15 @@ impl<F: FusedFuture + TryFuture> Future for FirstOk<F> {
64
57
}
65
58
66
59
let mut state = State :: NoErrors ;
67
- let this = self . get_mut ( ) ;
68
- for fut in this . futures . iter_mut ( ) {
60
+
61
+ for fut in self . get_mut ( ) . futures . iter_mut ( ) {
69
62
if !fut. is_terminated ( ) {
70
63
// Safety: we promise that the future is never moved out of the vec,
71
64
// and that the vec never reallocates once FirstOk has been created
72
65
// (specifically after the first poll)
73
66
let pinned = unsafe { Pin :: new_unchecked ( fut) } ;
74
67
match pinned. try_poll ( cx) {
75
- Poll :: Ready ( Ok ( out) ) => {
76
- // Safety: safe because vec clears in place
77
- this. futures . clear ( ) ;
78
- return Poll :: Ready ( Ok ( out) ) ;
79
- }
68
+ Poll :: Ready ( Ok ( out) ) => return Poll :: Ready ( Ok ( out) ) ,
80
69
Poll :: Ready ( Err ( err) ) => state. apply_error ( err) ,
81
70
Poll :: Pending => state. apply_pending ( ) ,
82
71
}
@@ -85,17 +74,21 @@ impl<F: FusedFuture + TryFuture> Future for FirstOk<F> {
85
74
86
75
match state {
87
76
SeenError ( err) => Poll :: Ready ( Err ( err) ) ,
88
- NoErrors | SeenPending => Poll :: Pending ,
77
+ SeenPending => Poll :: Pending ,
78
+ // This is unreachable unless every future in the vec returned
79
+ // is_terminated, which means that we must have returned Ready on
80
+ // a previous poll, or the vec is empty, which we disallow in the
81
+ // first_ok constructor, or that we were initialized with futures
82
+ // that have already returned Ready, which is possibly unsound
83
+ // (given !Unpin futures) but certainly breaks first_ok contract.
84
+ NoErrors => panic ! ( "All futures in the FirstOk terminated without a result being found. Did you re-poll after Ready?" ) ,
89
85
}
90
86
}
91
87
}
92
88
93
- impl < F : FusedFuture + TryFuture > FusedFuture for FirstOk < F > {
94
- #[ inline]
95
- fn is_terminated ( & self ) -> bool {
96
- self . futures . iter ( ) . all ( |fut| fut. is_terminated ( ) )
97
- }
98
- }
89
+ // We don't provide FusedFuture, because the overhead of implementing it (
90
+ // which requires clearing the vector after Ready is returned) is precisely
91
+ // the same as using .fuse()
99
92
100
93
impl < Fut : FusedFuture + TryFuture > FromIterator < Fut > for FirstOk < Fut > {
101
94
fn from_iter < T : IntoIterator < Item = Fut > > ( iter : T ) -> Self {
@@ -121,13 +114,21 @@ impl<Fut: FusedFuture + TryFuture> FromIterator<Fut> for FirstOk<Fut> {
121
114
///
122
115
/// # Panics
123
116
///
124
- /// This function will panic if the iterator specified contains no items.
117
+ /// This function will panic if the iterator specified contains no items, or
118
+ /// if any of the futures have already been terminated.
125
119
pub fn first_ok < I > ( futures : I ) -> FirstOk < I :: Item >
126
120
where
127
121
I : IntoIterator ,
128
122
I :: Item : FusedFuture + TryFuture ,
129
123
{
130
- let futures = Vec :: from_iter ( futures) ;
131
- assert ! ( !futures. is_empty( ) , "Need at least 1 future for first_ok" ) ;
124
+ let futures: Vec < _ > = futures
125
+ . into_iter ( )
126
+ . inspect ( |fut| {
127
+ assert ! ( !fut. is_terminated( ) , "Can't call first_ok with a terminated future" )
128
+ } )
129
+ . collect ( ) ;
130
+
131
+ assert ! ( !futures. is_empty( ) , "Need at least 1 non-terminated future for first_ok" ) ;
132
+
132
133
FirstOk { futures }
133
134
}
0 commit comments