|
1 |
| -//! Concurrency extensions for [`Future`][core::future::Future] and `Stream` |
2 |
| -//! (also known as [`AsyncIterator`][core::async_iter::AsyncIterator]). |
| 1 | +//! Performant, portable, structured concurrency operations for async Rust. It |
| 2 | +//! works with any runtime, does not erase lifetimes, always handles |
| 3 | +//! cancellation, and always returns output to the caller. |
3 | 4 | //!
|
4 |
| -//! Companion library for the "Futures Concurrency" blog post |
5 |
| -//! series: |
6 |
| -//! - [Futures Concurrency I: Introduction](https://blog.yoshuawuyts.com/futures-concurrency/) |
7 |
| -//! - [Futures Concurrency II: A Trait Approach](https://blog.yoshuawuyts.com/futures-concurrency-2/) |
8 |
| -//! - [Futures Concurrency III: `select!`](https://blog.yoshuawuyts.com/futures-concurrency-3/) |
9 |
| -//! - [Futures Concurrency IV: Join Semantics](https://blog.yoshuawuyts.com/futures-concurrency-4/) |
| 5 | +//! `futures-concurrency` provides concurrency operations for both groups of futures |
| 6 | +//! and streams. Both for bounded and unbounded sets of futures and streams. In both |
| 7 | +//! cases performance should be on par with, if not exceed conventional executor |
| 8 | +//! implementations. |
10 | 9 | //!
|
11 |
| -//! The purpose of this library is to serve as a staging ground for what |
12 |
| -//! eventually may become the futures concurrency methods provided by the |
13 |
| -//! stdlib. See the [`future`] and [`stream`] submodules for more. |
| 10 | +//! # Examples |
14 | 11 | //!
|
15 |
| -//! # Operations |
| 12 | +//! **Await multiple futures of different types** |
| 13 | +//! ```rust |
| 14 | +//! use futures_concurrency::prelude::*; |
| 15 | +//! use std::future; |
16 | 16 | //!
|
17 |
| -//! This library provides the following operations on arrays, vecs, and tuples: |
| 17 | +//! # futures::executor::block_on(async { |
| 18 | +//! let a = future::ready(1u8); |
| 19 | +//! let b = future::ready("hello"); |
| 20 | +//! let c = future::ready(3u16); |
| 21 | +//! assert_eq!((a, b, c).join().await, (1, "hello", 3)); |
| 22 | +//! # }); |
| 23 | +//! ``` |
18 | 24 | //!
|
19 |
| -//! - [`future::Join`]: Wait for all futures to complete. |
20 |
| -//! - [`future::TryJoin`]: Wait for all futures to complete successfully, or abort early on error. |
21 |
| -//! - [`future::Race`]: Wait for the first future to complete. |
22 |
| -//! - [`future::RaceOk`]: Wait for the first successful future to complete. |
23 |
| -//! - [`stream::Chain`]: Takes multiple streams and creates a new stream over all in sequence. |
24 |
| -//! - [`stream::Merge`]: Combines multiple streams into a single stream of all their outputs. |
25 |
| -//! - [`stream::Zip`]: ‘Zips up’ multiple streams into a single stream of pairs. |
| 25 | +//! **Concurrently process items in a collection** |
26 | 26 | //!
|
27 |
| -//! # Examples |
| 27 | +//! ```rust |
| 28 | +//! use futures_concurrency::prelude::*; |
| 29 | +//! |
| 30 | +//! # futures::executor::block_on(async { |
| 31 | +//! let v: Vec<_> = vec!["chashu", "nori"] |
| 32 | +//! .into_co_stream() |
| 33 | +//! .map(|msg| async move { format!("hello {msg}") }) |
| 34 | +//! .collect() |
| 35 | +//! .await; |
| 36 | +//! |
| 37 | +//! assert_eq!(v, &["hello chashu", "hello nori"]); |
| 38 | +//! # }); |
| 39 | +//! ``` |
| 40 | +//! |
| 41 | +//! **Access stack data outside the futures' scope** |
| 42 | +//! |
| 43 | +//! _Adapted from [`std::thread::scope`](https://doc.rust-lang.org/std/thread/fn.scope.html)._ |
28 | 44 | //!
|
29 |
| -//! Concurrently await multiple heterogenous futures: |
30 | 45 | //! ```rust
|
31 | 46 | //! use futures_concurrency::prelude::*;
|
32 |
| -//! use futures_lite::future::block_on; |
33 |
| -//! use std::future; |
34 | 47 | //!
|
35 |
| -//! block_on(async { |
36 |
| -//! let a = future::ready(1u8); |
37 |
| -//! let b = future::ready("hello"); |
38 |
| -//! let c = future::ready(3u16); |
39 |
| -//! assert_eq!((a, b, c).join().await, (1, "hello", 3)); |
40 |
| -//! }) |
| 48 | +//! # futures::executor::block_on(async { |
| 49 | +//! let mut container = vec![1, 2, 3]; |
| 50 | +//! let mut num = 0; |
| 51 | +//! |
| 52 | +//! let a = async { |
| 53 | +//! println!("hello from the first future"); |
| 54 | +//! dbg!(&container); |
| 55 | +//! }; |
| 56 | +//! |
| 57 | +//! let b = async { |
| 58 | +//! println!("hello from the second future"); |
| 59 | +//! num += container[0] + container[2]; |
| 60 | +//! }; |
| 61 | +//! |
| 62 | +//! println!("hello from the main future"); |
| 63 | +//! let _ = (a, b).join().await; |
| 64 | +//! container.push(4); |
| 65 | +//! assert_eq!(num, container.len()); |
| 66 | +//! # }); |
41 | 67 | //! ```
|
42 | 68 | //!
|
43 |
| -//! # Limitations |
| 69 | +//! # Operations |
| 70 | +//! |
| 71 | +//! ## Futures |
| 72 | +//! |
| 73 | +//! For futures which return a regular type `T` only the `join` and `race` |
| 74 | +//! operations are available. `join` waits for all futures to complete, while `race` |
| 75 | +//! will wait for the first future to complete. However for futures which return a |
| 76 | +//! `Try<Output = T>` two additional operations are available. The following table |
| 77 | +//! describes the behavior of concurrency operations for fallible futures: |
| 78 | +//! |
| 79 | +//! | | **Wait for all outputs** | **Wait for first output** | |
| 80 | +//! | -------------------------- | :----------------------- | :------------------------ | |
| 81 | +//! | **Continue on error** | `Future::join` | `Future::race_ok` | |
| 82 | +//! | **Short-circuit on error** | `Future::try_join` | `Future::race` | |
| 83 | +//! |
| 84 | +//! The following futures implementations are provided by `futures-concurrency`: |
| 85 | +//! - [`FutureGroup`][future::FutureGroup]: A growable group of futures which operate as a single unit. |
| 86 | +//! - `tuple`: [`join`][future::Join#impl-Join-for-(A,+B)], [`try_join`][future::TryJoin#impl-TryJoin-for-(A,+B)], [`race`][future::Race#impl-Race-for-(A,+B)], [`race_ok`][future::RaceOk#impl-RaceOk-for-(A,+B)] |
| 87 | +//! - `array`: [`join`][future::Join#impl-Join-for-\[Fut;+N\]], [`try_join`][future::TryJoin#impl-TryJoin-for-\[Fut;+N\]], [`race`][future::Race#impl-Race-for-\[Fut;+N\]], [`race_ok`][future::RaceOk#impl-RaceOk-for-\[Fut;+N\]] |
| 88 | +//! - `Vec`: [`join`][future::Join#impl-Join-for-Vec<Fut>], [`try_join`][future::TryJoin#impl-TryJoin-for-Vec<Fut>], [`race`][future::Race#impl-Race-for-Vec<Fut>], [`race_ok`][future::RaceOk#impl-RaceOk-for-Vec<Fut>] |
| 89 | +//! |
| 90 | +//! ## Streams |
| 91 | +//! |
| 92 | +//! Streams yield outputs one-by-one, which means that deciding to stop iterating is |
| 93 | +//! the same for fallible and infallible streams. The operations provided for |
| 94 | +//! streams can be categorized based on whether their inputs can be concurrently |
| 95 | +//! evaluated, and whether their outputs can be concurrently processed. |
| 96 | +//! |
| 97 | +//! Specifically in the case of `merge`, it takes `N` streams in, and yields items |
| 98 | +//! one-by-one as soon as any are available. This enables the output of individual |
| 99 | +//! streams to be concurrently processed by further operations later on. |
| 100 | +//! |
| 101 | +//! | | __Sequential output processing__ | __Concurrent output processing__ | |
| 102 | +//! | ------------------------------- | -------------------------------- | -------------------------------- | |
| 103 | +//! | __Sequential input evaluation__ | `Stream::chain` | *not yet available* ‡ | |
| 104 | +//! | __Concurrent input evaluation__ | `Stream::zip` | `Stream::merge` | |
| 105 | +//! |
| 106 | +//! ‡: _This could be addressed by a hypothetical `Stream::unzip` operation, |
| 107 | +//! however because we aspire for semantic compatibility with `std::iter::Iterator` |
| 108 | +//! in our operations, the path to adding it is currently unclear_. |
| 109 | +//! |
| 110 | +//! The following streams implementations are provided by `futures-concurrency`: |
| 111 | +//! |
| 112 | +//! - [`StreamGroup`][stream::StreamGroup]: A growable group of streams which operate as a single unit. |
| 113 | +//! - [`ConcurrentStream`][concurrent_stream::ConcurrentStream]: A trait for asynchronous streams which can concurrently process items. |
| 114 | +//! - `tuple`: [`chain`][stream::Chain#impl-Chain-for-(A,+B)], [`merge`][stream::Merge#impl-Merge-for-(A,+B)], [`zip`][stream::Zip#impl-Zip-for-(A,+B)] |
| 115 | +//! - `array`: [`chain`][stream::Chain#impl-Chain-for-\[Fut;+N\]], [`merge`][stream::Merge#impl-Merge-for-\[Fut;+N\]], [`zip`][stream::Zip#impl-Zip-for-\[Fut;+N\]] |
| 116 | +//! - `Vec`: [`chain`][stream::Chain#impl-Chain-for-Vec<Fut>], [`merge`][stream::Merge#impl-Merge-for-Vec<Fut>], [`zip`][stream::Zip#impl-Zip-for-Vec<Fut>] |
44 | 117 | //!
|
45 |
| -//! Because of orphan rules this library can't implement everything the stdlib |
46 |
| -//! can. The missing implementations are: |
| 118 | +//! # Runtime Support |
47 | 119 | //!
|
48 |
| -//! - `impl<T> IntoFuture for Vec<T>` |
49 |
| -//! - `impl<T, const N: usize> IntoFuture for [T; N]` |
50 |
| -//! - `impl<T..> IntoFuture for (T..)` |
51 |
| -//! - `impl<T> IntoAsyncIterator for Vec<T>` |
52 |
| -//! - `impl<T, const N: usize> IntoAsyncIterator for [T; N]` |
53 |
| -//! - `impl<T..> IntoAsyncIterator for (T..)` |
| 120 | +//! `futures-concurrency` does not depend on any runtime executor being present. |
| 121 | +//! This enables it to work out of the box with any async runtime, including: |
| 122 | +//! `tokio`, `async-std`, `smol`, `glommio`, and `monoio`. It also supports |
| 123 | +//! `#[no_std]` environments, allowing it to be used with embedded async |
| 124 | +//! runtimes such as `embassy`. |
54 | 125 | //!
|
55 |
| -//! This would enable containers of futures to directly be `.await`ed to get |
56 |
| -//! `merge` semantics. Or containers of async iterators to be passed directly to |
57 |
| -//! `for..await in` loops to be iterated over using `merge` semantics. This would |
58 |
| -//! remove the need to think of "merge" as a verb, and would enable treating |
59 |
| -//! sets of futures concurrently. |
| 126 | +//! # Feature Flags |
| 127 | +//! |
| 128 | +//! The `std` feature flag is enabled by default. To target `alloc` or `no_std` |
| 129 | +//! environments, you can enable the following configuration: |
| 130 | +//! |
| 131 | +//! ```toml |
| 132 | +//! [dependencies] |
| 133 | +//! # no_std |
| 134 | +//! futures-concurrency = { version = "7.5.0", default-features = false } |
| 135 | +//! |
| 136 | +//! # alloc |
| 137 | +//! futures-concurrency = { version = "7.5.0", default-features = false, features = ["alloc"] } |
| 138 | +//! ``` |
| 139 | +//! |
| 140 | +//! # Further Reading |
| 141 | +//! |
| 142 | +//! `futures-concurrency` has been developed over the span of several years. It is |
| 143 | +//! primarily maintained by Yosh Wuyts, a member of the Rust Async WG. You can read |
| 144 | +//! more about the development and ideas behind `futures-concurrency` here: |
| 145 | +//! |
| 146 | +//! - [Futures Concurrency I: Introduction](https://blog.yoshuawuyts.com/futures-concurrency/) |
| 147 | +//! - [Futures Concurrency II: A Trait Approach](https://blog.yoshuawuyts.com/futures-concurrency-2/) |
| 148 | +//! - [Futures Concurrency III: `select!`](https://blog.yoshuawuyts.com/futures-concurrency-3/) |
| 149 | +//! - [Futures Concurrency IV: Join Semantics](https://blog.yoshuawuyts.com/futures-concurrency-4/) |
60 | 150 |
|
61 | 151 | #![deny(missing_debug_implementations, nonstandard_style)]
|
62 | 152 | #![warn(missing_docs)]
|
|
0 commit comments