Skip to content

Commit c8f2fb0

Browse files
committed
doc(select): add alternatives section
1 parent a82bdee commit c8f2fb0

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

tokio/src/macros/select.rs

+94
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,100 @@ macro_rules! doc {
145145
/// application is shutting down, then you probably don't care that partially
146146
/// read data is lost.
147147
///
148+
/// # Alternatives from the Ecosystem
149+
///
150+
/// The `select!` macro is a versatile tool for working with multiple asynchronous
151+
/// branches, allowing tasks to run concurrently within the same thread. However,
152+
/// depending on your use case, ecosystem alternatives can provide additional
153+
/// benefits, such as more straightforward syntax, more transparent control flow or
154+
/// reducing the burned of cancellation safety or fuse semantics.
155+
///
156+
/// ## Merging Streams
157+
///
158+
/// For cases where `loop { select! { ... } }` is used to poll multiple tasks,
159+
/// stream merging offers a concise alternative, inherently handle cancellation-safe
160+
/// processing, removing the risk of data loss. Libraries like
161+
/// [`tokio-stream`](https://docs.rs/tokio-stream/latest/tokio_stream/) and
162+
/// [`futures-concurrency`](https://docs.rs/futures-concurrency/latest/futures_concurrency/)
163+
/// provide tools for merging streams and handling their outputs sequentially.
164+
///
165+
/// ### Example with `select!`
166+
///
167+
/// ```ignore
168+
/// // open our IO types
169+
/// let mut file = /* ... */;
170+
/// let mut channel = /* ... */;
171+
///
172+
/// async fn read_send(file: ..., channel: ...) { /* do work that is not cancel safe */ }
173+
///
174+
/// // This loop re-creates the `read_send` future on each iteration. That means if
175+
/// // `socket.read_packet()` completes, `read_send` may have read data from the file,
176+
/// // but not finished writing it to a channel, which can lead to data loss
177+
/// loop {
178+
/// select! {
179+
/// _ = read_send(&mut file, &mut channel) => {}, // data loss can happen here
180+
/// data = socket.read_packet() => {
181+
/// // ...
182+
/// }
183+
/// }
184+
/// }
185+
/// ```
186+
///
187+
/// ### Moving to `merge`
188+
///
189+
/// By using merge, you can unify multiple asynchronous tasks into a single stream,
190+
/// eliminating the need to manage tasks manually and reducing the risk of
191+
/// unintended behavior like data loss.
192+
///
193+
/// ```ignore
194+
/// let mut file = /* ... */;
195+
/// let mut channel = /* ... */;
196+
///
197+
/// enum Message {
198+
/// None,
199+
/// Data(Vec<u8>),
200+
/// }
201+
///
202+
/// async fn read_send(file: ..., channel: ...) { /* ... */ }
203+
///
204+
/// let a = futures_lite::stream::repeat_with(|| read_send(&mut file, &mut channel)).map(Message::None);
205+
/// let b = futures_lite::stream::repeat_with(|| socket.read_packet()).map(Message::Data);
206+
///
207+
/// let mut s = pin!(a.merge(b));
208+
/// while let Some(msg) = s.next().await {
209+
/// match msg {
210+
/// Message::None => continue,
211+
/// Message::Data(data) => /*...*/ ,
212+
/// }
213+
/// }
214+
/// ```
215+
///
216+
/// ## Racing Futures
217+
///
218+
/// If you need to wait for the first completion among several asynchronous tasks,
219+
/// ecosystem utilities such as
220+
/// [`futures-lite`](https://docs.rs/futures-lite/latest/futures_lite/) or
221+
/// [`futures-concurrency`](https://docs.rs/futures-concurrency/latest/futures_concurrency/)
222+
/// provide streamlined syntax for racing futures:
223+
///
224+
/// - [`futures-lite::future::or`](https://docs.rs/futures-lite/latest/futures_lite/future/fn.or.html)
225+
/// - [`futures-lite::future::race`](https://docs.rs/futures-lite/latest/futures_lite/future/fn.race.html)
226+
/// - [`futures_concurrency::future::Race`](https://docs.rs/futures-concurrency/latest/futures_concurrency/future/trait.Race.html)
227+
///
228+
/// ```ignore
229+
/// let result = async {
230+
/// futures_lite::future::race(
231+
/// async { /* Task A */ },
232+
/// async { /* Task B */ },
233+
/// ).await
234+
/// };
235+
///
236+
/// match result {
237+
/// Ok(output) => println!("First task completed with: {output}"),
238+
/// Err(err) => eprintln!("Error occurred: {err}"),
239+
/// }
240+
/// ```
241+
///
148242
/// # Examples
149243
///
150244
/// Basic select with two branches.

0 commit comments

Comments
 (0)