From 862687835cbbc930967218cd87fe7dd43fdd694f Mon Sep 17 00:00:00 2001 From: Oleksandr Babak Date: Sat, 18 Jan 2025 15:26:46 +0100 Subject: [PATCH] doc(select): add alternatives section --- tokio/src/macros/select.rs | 94 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tokio/src/macros/select.rs b/tokio/src/macros/select.rs index d1e59d34736..7edfb76a1f2 100644 --- a/tokio/src/macros/select.rs +++ b/tokio/src/macros/select.rs @@ -145,6 +145,100 @@ macro_rules! doc { /// application is shutting down, then you probably don't care that partially /// read data is lost. /// + /// # Alternatives from the Ecosystem + /// + /// The `select!` macro is a versatile tool for working with multiple asynchronous + /// branches, allowing tasks to run concurrently within the same thread. However, + /// depending on your use case, ecosystem alternatives can provide additional + /// benefits, such as more straightforward syntax, more transparent control flow or + /// reducing the burned of cancellation safety or fuse semantics. + /// + /// ## Merging Streams + /// + /// For cases where `loop { select! { ... } }` is used to poll multiple tasks, + /// stream merging offers a concise alternative, inherently handle cancellation-safe + /// processing, removing the risk of data loss. Libraries like + /// [`tokio-stream`](https://docs.rs/tokio-stream/latest/tokio_stream/) and + /// [`futures-concurrency`](https://docs.rs/futures-concurrency/latest/futures_concurrency/) + /// provide tools for merging streams and handling their outputs sequentially. + /// + /// ### Example with `select!` + /// + /// ```no_run + /// // open our IO types + /// let mut file = /* ... */; + /// let mut channel = /* ... */; + /// + /// async fn read_send(file: ..., channel: ...) { /*...*/ } + /// + /// // This loop re-creates the `read_send` future on each iteration. That means if + /// // `socket.read_packet` completes, `read_send` may have read data from the file, + /// // but not finished writing it to a channel, which can lead to data loss + /// loop { + /// select! { + /// _ = read_send(&mut file, &mut channel) => {}, // data loss can happen here + /// data = socket.read_packet() => { + /// // ... + /// } + /// } + /// } + /// ``` + /// + /// ### Moving to `merge` + /// + /// By using merge, you can unify multiple asynchronous tasks into a single stream, + /// eliminating the need to manage tasks manually and reducing the risk of + /// unintended behavior like data loss. + /// + /// ``no_run + /// let mut file = /* ... */; + /// let mut channel = /* ... */; + /// + /// enum Message { + /// None, + /// Data(Vec), + /// } + /// + /// async fn read_send(file: ..., channel: ...) { /*...*/ } + /// + /// let a = futures_lite::stream::repeat_with(|| read_send(&mut file, &mut channel)).map(Message::None); + /// let b = futures_lite::stream::repeat_with(|| socket.read_packet()).map(Message::Data); + /// + /// let mut s = a.merge(b); + /// while let Some(msg) = s.next().await { + /// match msg { + /// Message::None => continue, + /// Message::Data(data) => /*...*/ , + /// } + /// } + /// ``` + /// + /// ## Racing Futures + /// + /// If you need to wait for the first completion among several asynchronous tasks, + /// ecosystem utilities such as + /// [`futures-lite`](https://docs.rs/futures-lite/latest/futures_lite/) or + /// [`futures-concurrency`](https://docs.rs/futures-concurrency/latest/futures_concurrency/) + /// provide streamlined syntax for racing futures: + /// + /// - [`futures-lite::future::or`](https://docs.rs/futures-lite/latest/futures_lite/future/fn.or.html) + /// - [`futures-lite::future::race`](https://docs.rs/futures-lite/latest/futures_lite/future/fn.race.html) + /// - [`futures_concurrency::future::Race`](https://docs.rs/futures-concurrency/latest/futures_concurrency/future/trait.Race.html) + /// + /// ```no_run + /// let result = async { + /// futures_lite::future::race( + /// async { /* Task A */ }, + /// async { /* Task B */ }, + /// ).await + /// }; + /// + /// match result { + /// Ok(output) => println!("First task completed with: {output}"), + /// Err(err) => eprintln!("Error occurred: {err}"), + /// } + /// ``` + /// /// # Examples /// /// Basic select with two branches.