File tree Expand file tree Collapse file tree 3 files changed +44
-6
lines changed
futures-util/src/async_await Expand file tree Collapse file tree 3 files changed +44
-6
lines changed Original file line number Diff line number Diff line change @@ -172,13 +172,25 @@ pub fn select(input: TokenStream) -> TokenStream {
172
172
. zip ( variant_names. iter ( ) )
173
173
. map ( |( expr, variant_name) | {
174
174
match expr {
175
- // Don't bind futures that are already a path.
176
- // This prevents creating redundant stack space
177
- // for them.
178
- syn:: Expr :: Path ( path) => path,
175
+ syn:: Expr :: Path ( path) => {
176
+ // Don't bind futures that are already a path.
177
+ // This prevents creating redundant stack space
178
+ // for them.
179
+ path
180
+ } ,
179
181
_ => {
182
+ // Bind and pin the resulting Future on the stack. This is
183
+ // necessary to support direct select! calls on !Unpin
184
+ // Futures.
185
+ // Safety: This is safe since the lifetime of the Future
186
+ // is totally constraint to the lifetime of the select!
187
+ // expression, and the Future can't get moved inside it
188
+ // (it is shadowed).
180
189
future_let_bindings. push ( quote ! {
181
190
let mut #variant_name = #expr;
191
+ let mut #variant_name = unsafe {
192
+ :: core:: pin:: Pin :: new_unchecked( & mut #variant_name)
193
+ } ;
182
194
} ) ;
183
195
parse_quote ! { #variant_name }
184
196
}
Original file line number Diff line number Diff line change @@ -8,8 +8,15 @@ macro_rules! document_select_macro {
8
8
( $item: item) => {
9
9
/// Polls multiple futures and streams simultaneously, executing the branch
10
10
/// for the future that finishes first. If multiple futures are ready,
11
- /// one will be pseudo-randomly selected at runtime. Futures passed to
12
- /// `select!` must be `Unpin` and implement `FusedFuture`.
11
+ /// one will be pseudo-randomly selected at runtime. Futures directly
12
+ /// passed to `select!` must be `Unpin` and implement `FusedFuture`.
13
+ ///
14
+ /// If an expression which yields a `Future` is passed to `select!`
15
+ /// (e.g. an `async fn` call) instead of a `Future` directly the `Unpin`
16
+ /// requirement is relaxed, since the macro will pin the resulting `Future`
17
+ /// on the stack. However the `Future` returned by the expression must
18
+ /// still implement `FusedFuture`.
19
+ ///
13
20
/// Futures and streams which are not already fused can be fused using the
14
21
/// `.fuse()` method. Note, though, that fusing a future or stream directly
15
22
/// in the call to `select!` will not be enough to prevent it from being
Original file line number Diff line number Diff line change @@ -161,6 +161,25 @@ fn select_size() {
161
161
assert_eq ! ( :: std:: mem:: size_of_val( & fut) , 40 ) ;
162
162
}
163
163
164
+ #[ test]
165
+ fn select_on_non_unpin_expressions ( ) {
166
+ // The returned Future is !Unpin
167
+ let make_non_unpin_fut = || { async {
168
+ 5
169
+ } } ;
170
+
171
+ let res = block_on ( async {
172
+ let select_res;
173
+ select ! {
174
+ value_1 = make_non_unpin_fut( ) . fuse( ) => { select_res = value_1 } ,
175
+ value_2 = make_non_unpin_fut( ) . fuse( ) => { select_res = value_2 } ,
176
+ default => { select_res = 7 } ,
177
+ } ;
178
+ select_res
179
+ } ) ;
180
+ assert_eq ! ( res, 5 ) ;
181
+ }
182
+
164
183
#[ test]
165
184
fn join_size ( ) {
166
185
let fut = async {
You can’t perform that action at this time.
0 commit comments