Skip to content

Commit 3953c7d

Browse files
Matthias247cramertj
authored andcommitted
Improve documentation for select!
1 parent 2137a2e commit 3953c7d

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

futures-select-macro/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ pub fn select(input: TokenStream) -> TokenStream {
176176
// Don't bind futures that are already a path.
177177
// This prevents creating redundant stack space
178178
// for them.
179+
// Passing Futures by path requires those Futures to implement Unpin.
179180
path
180181
},
181182
_ => {

futures-util/src/async_await/select_mod.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ macro_rules! document_select_macro {
1212
/// passed to `select!` must be `Unpin` and implement `FusedFuture`.
1313
///
1414
/// 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`
15+
/// (e.g. an `async fn` call) instead of a `Future` by name the `Unpin`
1616
/// requirement is relaxed, since the macro will pin the resulting `Future`
1717
/// on the stack. However the `Future` returned by the expression must
18-
/// still implement `FusedFuture`.
18+
/// still implement `FusedFuture`. This difference is presented
1919
///
2020
/// Futures and streams which are not already fused can be fused using the
2121
/// `.fuse()` method. Note, though, that fusing a future or stream directly
@@ -24,8 +24,9 @@ macro_rules! document_select_macro {
2424
/// `select!`ing in a loop, users should take care to `fuse()` outside of
2525
/// the loop.
2626
///
27-
/// `select!` can select over futures with different output types, but each
28-
/// branch has to have the same return type.
27+
/// `select!` can be used as an expression and will return the return
28+
/// value of the selected branch. For this reason the return type of every
29+
/// branch in a `select!` must be the same.
2930
///
3031
/// This macro is only usable inside of async functions, closures, and blocks.
3132
/// It is also gated behind the `async-await` feature of this library, which is
@@ -63,11 +64,67 @@ macro_rules! document_select_macro {
6364
/// # });
6465
/// ```
6566
///
67+
/// As described earlier, `select` can directly select on expressions
68+
/// which return `Future`s - even if those do not implement `Unpin`:
69+
///
70+
/// ```
71+
/// # futures::executor::block_on(async {
72+
/// use futures::future::FutureExt;
73+
/// use futures::select;
74+
///
75+
/// // Calling the following async fn returns a Future which does not
76+
/// // implement Unpin
77+
/// async fn async_identity_fn(arg: usize) -> usize {
78+
/// arg
79+
/// }
80+
///
81+
/// let res = select! {
82+
/// a_res = async_identity_fn(62).fuse() => a_res + 1,
83+
/// b_res = async_identity_fn(13).fuse() => b_res,
84+
/// };
85+
/// assert!(res == 63 || res == 12);
86+
/// # });
87+
/// ```
88+
///
89+
/// If a similar async function is called outside of `select` to produce
90+
/// a `Future`, the `Future` must be pinned in order to be able to pass
91+
/// it to `select`. This can be achieved via `Box::pin` for pinning a
92+
/// `Future` on the heap or the `pin_mut!` macro for pinning a `Future`
93+
/// on the stack.
94+
///
95+
/// ```
96+
/// # futures::executor::block_on(async {
97+
/// use futures::future::FutureExt;
98+
/// use futures::select;
99+
/// use pin_utils::pin_mut;
100+
///
101+
/// // Calling the following async fn returns a Future which does not
102+
/// // implement Unpin
103+
/// async fn async_identity_fn(arg: usize) -> usize {
104+
/// arg
105+
/// }
106+
///
107+
/// let fut_1 = async_identity_fn(1).fuse();
108+
/// let fut_2 = async_identity_fn(2).fuse();
109+
/// let mut fut_1 = Box::pin(fut_1); // Pins the Future on the heap
110+
/// pin_mut!(fut_2); // Pins the Future on the stack
111+
///
112+
/// let res = select! {
113+
/// a_res = fut_1 => a_res,
114+
/// b_res = fut_2 => b_res,
115+
/// };
116+
/// assert!(res == 1 || res == 2);
117+
/// # });
118+
/// ```
119+
///
66120
/// `select` also accepts a `complete` branch and a `default` branch.
67121
/// `complete` will run if all futures and streams have already been
68122
/// exhausted. `default` will run if no futures or streams are
69123
/// immediately ready. `complete` takes priority over `default` in
70124
/// the case where all futures have completed.
125+
/// A motivating use-case for passing `Future`s by name as well as for
126+
/// `complete` blocks is to call `select!` in a loop, which is
127+
/// demonstrated in the following example:
71128
///
72129
/// ```
73130
/// # futures::executor::block_on(async {

0 commit comments

Comments
 (0)