From 8e0c9956f5f8b73a62abc8a52672ee36bab1c88c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 14 Dec 2021 13:21:05 +0100 Subject: [PATCH] Rename 2996-async-stream to 2996-async-iterator --- ...async-stream.md => 2996-async-iterator.md} | 301 +++++++++--------- 1 file changed, 151 insertions(+), 150 deletions(-) rename text/{2996-async-stream.md => 2996-async-iterator.md} (60%) diff --git a/text/2996-async-stream.md b/text/2996-async-iterator.md similarity index 60% rename from text/2996-async-stream.md rename to text/2996-async-iterator.md index eab2acc49f0..1bb5589efca 100644 --- a/text/2996-async-stream.md +++ b/text/2996-async-iterator.md @@ -1,4 +1,4 @@ -- Feature Name: `async_stream` +- Feature Name: `async_iterator` - Start Date: 2020-09-29 - RFC PR: [rust-lang/rfcs#2996](https://github.com/rust-lang/rfcs/pull/2996) - Rust Issue: [rust-lang/rust#79024](https://github.com/rust-lang/rust/issues/79024) @@ -6,27 +6,28 @@ # Summary [summary]: #summary -Introduce the `Stream` trait into the standard library, using the +Introduce the `AsyncIterator` trait into the standard library, using the design from `futures`. Redirect the `Stream` trait definition in the -`futures-core` crate (which is "pub-used" by the `futures` crate) to the standard library. +`futures-core` crate (which is "pub-used" by the `futures` crate) to the +`AsyncIterator` trait in the standard library. # Motivation [motivation]: #motivation -Streams are a core async abstraction. These behave similarly to `Iterator`, +Async iterators are a core async abstraction. These behave similarly to `Iterator`, but rather than blocking between each item yield, it allows other tasks to run while it waits. People can do this currently using the `Stream` trait defined in the [futures](https://crates.io/crates/futures) crate. However, we would like -to add `Stream` to the standard library. +to add `AsyncIterator` to the standard library as `AsyncIterator`. -Including `Stream` in the standard library would clarify the stability guarantees of the trait. For example, if [Tokio](https://tokio.rs/) +Including `AsyncIterator` in the standard library would clarify the stability guarantees of the trait. For example, if [Tokio](https://tokio.rs/) wishes to declare a [5 year stability period](http://smallcultfollowing.com/babysteps/blog/2020/02/11/async-interview-6-eliza-weisman/#communicating-stability), -having the `Stream` trait in the standard library means there are no concerns +having the `AsyncIterator` trait in the standard library means there are no concerns about the trait changing during that time ([citation](http://smallcultfollowing.com/babysteps/blog/2019/12/23/async-interview-3-carl-lerche/#what-should-we-do-next-stabilize-stream)). -## Examples of current crates that are consuming streams +## Examples of current crates that are consuming async iterators ### async-h1 @@ -34,36 +35,36 @@ about the trait changing during that time ([citation](http://smallcultfollowing. ### async-sse -* [async-sse](https://docs.rs/async-sse/) parses incoming buffers into a stream of messages. +* [async-sse](https://docs.rs/async-sse/) parses incoming buffers into an async iterator of messages. ## Why a shared trait? -We eventually want dedicated syntax for working with streams, which will require a shared trait. -This includes a trait for producing streams and a trait for consuming streams. +We eventually want dedicated syntax for working with async iterators, which will require a shared trait. +This includes a trait for producing async iterators and a trait for consuming async iterators. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -A "stream" is the async version of an [iterator]. +An "async iterator" is the async version of an [iterator]. -The `Iterator` trait includes a `next` method, which computes and returns the next item in the sequence. The `Stream` trait includes the `poll_next` method to assist with defining a stream. In the future, we should add a `next` method for use when consuming and interacting with a stream (see the [Future possiblilities][future-possibilities] section later in this RFC). +The `Iterator` trait includes a `next` method, which computes and returns the next item in the sequence. The `AsyncIterator` trait includes the `poll_next` method to assist with defining a async iterator. In the future, we should add a `next` method for use when consuming and interacting with a async iterator (see the [Future possiblilities][future-possibilities] section later in this RFC). ## poll_next method -When implementing a `Stream`, users will define a `poll_next` method. +When implementing a `AsyncIterator`, users will define a `poll_next` method. The `poll_next` method asks if the next item is ready. If so, it returns the item. Otherwise, `poll_next` will return [`Poll::Pending`]. Just as with a [`Future`], returning [`Poll::Pending`] -implies that the stream has arranged for the current task to be re-awoken when the data is ready. +implies that the async iterator has arranged for the current task to be re-awoken when the data is ready. [iterator]: https://doc.rust-lang.org/std/iter/trait.Iterator.html [`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html [`Poll::Pending`]: https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Pending ```rust -// Defined in std::stream module -pub trait Stream { +// Defined in std::async_iter module +pub trait AsyncIterator { // Core items: type Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; @@ -79,8 +80,8 @@ pub trait Stream { The arguments to `poll_next` match that of the [`Future::poll`] method: * The self must be a pinned reference, ensuring both unique access to - the stream and that the stream value itself will not move. Pinning - allows the stream to save pointers into itself when it suspends, + the async iterator and that the async iterator value itself will not move. Pinning + allows the async iterator to save pointers into itself when it suspends, which will be required to support generator syntax at some point. * The [context] `cx` defines details of the current task. In particular, it gives access to the [`Waker`] for the task, which will allow the @@ -93,23 +94,23 @@ The arguments to `poll_next` match that of the [`Future::poll`] method: ### Usage -A user could create a stream as follows (Example taken from @yoshuawuyt's [implementation pull request](https://github.com/rust-lang/rust/pull/79023)). +A user could create an async iterator as follows (Example taken from @yoshuawuyts' [implementation pull request](https://github.com/rust-lang/rust/pull/79023)). -Creating a stream involves two steps: creating a `struct` to - hold the stream's state, and then implementing `Stream` for that +Creating an async iterator involves two steps: creating a `struct` to + hold the async iterator's state, and then implementing `AsyncIterator` for that `struct`. - Let's make a stream named `Counter` which counts from `1` to `5`: + Let's make an async iterator named `Counter` which counts from `1` to `5`: ```rust -#![feature(async_stream)] -# use core::stream::Stream; +#![feature(async_iterator)] +# use core::async_iter::AsyncIterator; # use core::task::{Context, Poll}; # use core::pin::Pin; // First, the struct: -/// A stream which counts from one to five +/// An async iterator which counts from one to five struct Counter { count: usize, } @@ -123,9 +124,9 @@ impl Counter { } } -// Then, we implement `Stream` for our `Counter`: +// Then, we implement `AsyncIterator` for our `Counter`: -impl Stream for Counter { +impl AsyncIterator for Counter { // we will be counting with usize type Item = usize; @@ -149,33 +150,33 @@ impl Stream for Counter { There are a number of simple "bridge" impls that are also provided: ```rust -impl Stream for Box +impl AsyncIterator for Box where - S: Stream + Unpin + ?Sized, + S: AsyncIterator + Unpin + ?Sized, { - type Item = ::Item + type Item = ::Item } -impl Stream for &mut S +impl AsyncIterator for &mut S where - S: Stream + Unpin + ?Sized, + S: AsyncIterator + Unpin + ?Sized, { - type Item = ::Item; + type Item = ::Item; } -impl Stream for Pin

+impl AsyncIterator for Pin

where P: DerefMut + Unpin, - T: Stream, + T: AsyncIterator, { - type Item = ::Item; + type Item = ::Item; } -impl Stream for AssertUnwindSafe +impl AsyncIterator for AssertUnwindSafe where - S: Stream, + S: AsyncIterator, { - type Item = ::Item; + type Item = ::Item; } ``` @@ -185,20 +186,20 @@ where This section goes into details about various aspects of the design and why they ended up the way they did. -## Where does `Stream` live in the std lib? +## Where does `AsyncIterator` live in the std lib? -`Stream` will live in the `core::stream` module and be re-exported as `std::stream`. +`AsyncIterator` will live in the `core::async_iter` module and be re-exported as `std::async_iter`. It is possible that it could live in another area as well, though this follows the pattern of `core::future`. ## Why use a `poll` method? -An alternative design for the stream trait would be to have a trait +An alternative design for the async iterator trait would be to have a trait that defines an async `next` method: ```rust -trait Stream { +trait AsyncIterator { type Item; async fn next(&mut self) -> Option; @@ -212,18 +213,18 @@ before they can be added. Moreover, it is not clear yet how to make traits that contain async functions be `dyn` safe, and it is important to be able to pass around `dyn -Stream` values without the need to monomorphize the functions that work +AsyncIterator` values without the need to monomorphize the functions that work with them. Unfortunately, the use of poll does mean that it is harder to write -stream implementations. The long-term fix for this, discussed in the [Future possiblilities][future-possibilities] section, is dedicated [generator syntax]. +async iterator implementations. The long-term fix for this, discussed in the [Future possiblilities][future-possibilities] section, is dedicated [generator syntax]. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -## Where should stream live? +## Where should async iterator live? -As mentioned above, `core::stream` is analogous to `core::future`. But, do we want to find +As mentioned above, `core::async_iter` is analogous to `core::future`. But, do we want to find some other naming scheme that can scale up to other future additions, such as io traits or channels? # Future possibilities @@ -231,26 +232,26 @@ some other naming scheme that can scale up to other future additions, such as io ## Next method -While users will be able to implement a `Stream` as defined in this RFC, they will not have a way to interact with it in the core library. As soon as we figure out a way to do it in an object safe manner, we should add a `next` method either in the `Stream` trait or elsewhere. +While users will be able to implement a `AsyncIterator` as defined in this RFC, they will not have a way to interact with it in the core library. As soon as we figure out a way to do it in an object safe manner, we should add a `next` method either in the `AsyncIterator` trait or elsewhere. -The `Iterator` trait includes a `next` method, which computes and returns the next item in the sequence. We should also implement a `next` method for `Stream`, similar to [the implementation in the futures-util crate](https://docs.rs/futures-util/0.3.5/src/futures_util/stream/stream/next.rs.html#10-12). +The `Iterator` trait includes a `next` method, which computes and returns the next item in the sequence. We should also implement a `next` method for `AsyncIterator`, similar to [the implementation in the futures-util crate](https://docs.rs/futures-util/0.3.5/src/futures_util/stream/stream/next.rs.html#10-12). The core `poll_next` method is unergonomic; it does not let you iterate -over the items coming out of the stream. Therefore, we include a few minimal +over the items coming out of the async iterator. Therefore, we include a few minimal convenience methods that are not dependent on any unstable features, such as `next`. -As @yoshuawuyts states in their [pull request which adds `core::stream::Stream` to the standard library](https://github.com/rust-lang/rust/pull/79023): +As @yoshuawuyts states in their [pull request which adds `core::async_iter::AsyncIterator` to the standard library](https://github.com/rust-lang/rust/pull/79023): -Unlike `Iterator`, `Stream` makes a distinction between the `poll_next` -method which is used when implementing a `Stream`, and the `next` method -which is used when consuming a stream. Consumers of `Stream` only need to +Unlike `Iterator`, `AsyncIterator` makes a distinction between the `poll_next` +method which is used when implementing a `AsyncIterator`, and the `next` method +which is used when consuming an async iterator. Consumers of `AsyncIterator` only need to consider `next`, which when called, returns a future which yields `Option`. The future returned by `next` will yield `Some(Item)` as long as there are elements, and once they've all been exhausted, will yield `None` to indicate that iteration is finished. If we're waiting on something asynchronous to -resolve, the future will wait until the stream is ready to yield again. +resolve, the future will wait until the async iterator is ready to yield again. As defined in the [`Future` docs](https://doc.rust-lang.org/stable/std/future/trait.Future.html): @@ -260,12 +261,12 @@ This is similar to the `Future` trait. The `Future::poll` method is rarely calle directly, it is almost always used to implement other Futures. Interacting with futures is done through `async/await`. -We need something like the `next()` method in order to iterate over the stream directly in an `async` block or function. It is essentially an adapter from `Stream` to `Future`. +We need something like the `next()` method in order to iterate over the async iterator directly in an `async` block or function. It is essentially an adapter from `AsyncIterator` to `Future`. This would allow a user to await on a future: ```rust -while let Some(v) = stream.next().await { +while let Some(v) = async_iter.next().await { } ``` @@ -285,7 +286,7 @@ while let Some(x) = s.next().await.transpose()? ### More Usage Examples -Using the example of `Stream` implemented on a struct called `Counter`, the user would interact with the stream like so: +Using the example of `AsyncIterator` implemented on a struct called `Counter`, the user would interact with the async iterator like so: ```rust let mut counter = Counter::new(); @@ -310,23 +311,23 @@ println!("{}", x); This would print `1` through `5`, each on their own line. -An earlier draft of the RFC prescribed an implementation of the `next` method on the `Stream` trait. Unfortunately, as detailed in [this comment](https://github.com/rust-lang/rust/pull/79023#discussion_r547425181), it made the stream non-object safe. More experimentation is required - and it may need to be an unstable language feature for more testing before it can be added to core. +An earlier draft of the RFC prescribed an implementation of the `next` method on the `AsyncIterator` trait. Unfortunately, as detailed in [this comment](https://github.com/rust-lang/rust/pull/79023#discussion_r547425181), it made the async iterator non-object safe. More experimentation is required - and it may need to be an unstable language feature for more testing before it can be added to core. ## More Convenience methods The `Iterator` trait also defines a number of useful combinators, like -`map`. The `Stream` trait being proposed here does not include any +`map`. The `AsyncIterator` trait being proposed here does not include any such conveniences. Instead, they are available via extension traits, -such as the [`StreamExt`] trait offered by the [`futures`] crate. +such as the [`AsyncIteratorExt`] trait offered by the [`futures`] crate. -[`StreamExt`]: https://docs.rs/futures/0.3.5/futures/stream/trait.StreamExt.html +[`AsyncIteratorExt`]: https://docs.rs/futures/0.3.5/futures/stream/trait.AsyncIteratorExt.html [`futures`]: https://crates.io/crates/futures The reason that we have chosen to exclude combinators is that a number of them would require access to async closures. As of this writing, async closures are unstable and there are a number of [outstanding design issues] to be resolved before they are added. Therefore, we've -decided to enable progress on the stream trait by stabilizing a core, +decided to enable progress on the async iterator trait by stabilizing a core, and to come back to the problem of extending it with combinators. [outstanding design issues]: https://rust-lang.github.io/wg-async-foundations/design_docs/async_closures.html @@ -344,9 +345,9 @@ existing code, perhaps as part of an edition migration. Designing such a migration feature is out of scope for this RFC. -## IntoStream / FromStream traits +## IntoAsyncIterator / FromAsyncIterator traits -### IntoStream +### IntoAsyncIterator **Iterators** @@ -398,35 +399,35 @@ let values = vec![1, 2, 3, 4, 5]; * `for x in &iter` uses `impl IntoIterator for &T` * `for x in &mut iter` uses `impl IntoIterator for &mut T` -**Streams** +**AsyncIterators** -We may want a trait similar to this for `Stream`. The `IntoStream` trait would provide a way to convert something into a `Stream`. +We may want a trait similar to this for `AsyncIterator`. The `IntoAsyncIterator` trait would provide a way to convert something into a `AsyncIterator`. This trait could look like this: ```rust -pub trait IntoStream +pub trait IntoAsyncIterator where - ::Item == Self::Item, + ::Item == Self::Item, { type Item; - type IntoStream: Stream; + type IntoAsyncIterator: AsyncIterator; - fn into_stream(self) -> Self::IntoStream; + fn into_async_iter(self) -> Self::IntoAsyncIterator; } ``` -This trait (as expressed by @taiki-e in [a comment on a draft of this RFC](https://github.com/rust-lang/wg-async-foundations/pull/15/files#r449880986)) makes it easy to write streams in combination with [async stream](https://github.com/taiki-e/futures-async-stream). For example: +This trait (as expressed by @taiki-e in [a comment on a draft of this RFC](https://github.com/rust-lang/wg-async-foundations/pull/15/files#r449880986)) makes it easy to write streams in combination with [async iterator](https://github.com/taiki-e/futures-async-stream). For example: ```rust type S(usize); -impl IntoStream for S { +impl IntoAsyncIterator for S { type Item = usize; - type IntoStream: impl Stream; + type IntoAsyncIterator: impl AsyncIterator; - fn into_stream(self) -> Self::IntoStream { + fn into_async_iter(self) -> Self::IntoAsyncIterator { #[stream] async move { for i in 0..self.0 { @@ -437,7 +438,7 @@ impl IntoStream for S { } ``` -### FromStream +### FromAsyncIterator **Iterators** @@ -475,44 +476,44 @@ let doubled: Vec = a.iter() ``` -**Streams** +**Async Iterators** -We may want a trait similar to this for `Stream`. The `FromStream` trait would provide a way to convert a `Stream` into another type. +We may want a trait similar to this for `AsyncIterator`. The `FromAsyncIterator` trait would provide a way to convert a `AsyncIterator` into another type. This trait could look like this: ```rust -pub trait FromStream { - async fn from_stream(stream: T) -> Self +pub trait FromAsyncIterator { + async fn from_async_iter(iter: T) -> Self where - T: IntoStream; + T: IntoAsyncIterator; } ``` -We could potentially include a collect method for Stream as well. +We could potentially include a collect method for AsyncIterator as well. ```rust -pub trait Stream { +pub trait AsyncIterator { async fn collect(self) -> B where - B: FromStream, + B: FromAsyncIterator, { ... } } ``` When drafting this RFC, there was [discussion](https://github.com/rust-lang/wg-async-foundations/pull/15#discussion_r451182595) -about whether to implement from_stream for all T where `T: FromIterator` as well. -`FromStream` is perhaps more general than `FromIterator` because the await point is allowed to suspend execution of the +about whether to implement from_async_iter for all T where `T: FromIterator` as well. +`FromAsyncIterator` is perhaps more general than `FromIterator` because the await point is allowed to suspend execution of the current function, but doesn't have to. Therefore, many (if not all) existing impls of `FromIterator` would work -for `FromStream` as well. While this would be a good point for a future discussion, it is not in the scope of this RFC. +for `FromAsyncIterator` as well. While this would be a good point for a future discussion, it is not in the scope of this RFC. -## Converting an Iterator to a Stream +## Converting an Iterator to a AsyncIterator -If a user wishes to convert an Iterator to a Stream, they may not be able to use IntoStream because a blanked impl for Iterator would conflict with more specific impls they may wish to write. Having a function that takes an `impl Iterator` and returns an `impl Stream` would be quite helpful. +If a user wishes to convert an Iterator to a AsyncIterator, they may not be able to use IntoAsyncIterator because a blanked impl for Iterator would conflict with more specific impls they may wish to write. Having a function that takes an `impl Iterator` and returns an `impl AsyncIterator` would be quite helpful. -The [async-std](https://github.com/async-rs/async-std) crate has [stream::from_iter](https://docs.rs/async-std/1.6.5/async_std/stream/fn.from_iter.html). The [futures-rs](https://github.com/rust-lang/futures-rs) crate has [stream::iter](https://docs.rs/futures/0.3.5/futures/stream/fn.iter.html). Either of these approaches could work once we expose `Stream` in the standard library. +The [async-std](https://github.com/async-rs/async-std) crate has [stream::from_iter](https://docs.rs/async-std/1.6.5/async_std/stream/fn.from_iter.html). The [futures-rs](https://github.com/rust-lang/futures-rs) crate has [stream::iter](https://docs.rs/futures/0.3.5/futures/stream/fn.iter.html). Either of these approaches could work once we expose `AsyncIterator` in the standard library. -Adding this functionality is out of the scope of this RFC, but is something we should revisit once `Stream` is in the standard library. +Adding this functionality is out of the scope of this RFC, but is something we should revisit once `AsyncIterator` is in the standard library. ## Other Traits @@ -520,75 +521,75 @@ Eventually, we may also want to add some (if not all) of the roster of traits we [async_std::stream](https://docs.rs/async-std/1.6.0/async_std/stream/index.html) has created several async counterparts to the traits in [std::iter](https://doc.rust-lang.org/std/iter/). These include: -* DoubleEndedStream: A stream able to yield elements from both ends. -* ExactSizeStream: A stream that knows its exact length. -* Extend: Extends a collection with the contents of a stream. -* FromStream: Conversion from a Stream. -* FusedStream: A stream that always continues to yield None when exhausted. -* IntoStream: Conversion into a Stream. -* Product: Trait to represent types that can be created by multiplying the elements of a stream. -* Stream: An asynchronous stream of values. -* Sum: Trait to represent types that can be created by summing up a stream. +* DoubleEndedAsyncIterator: An async iterator able to yield elements from both ends. +* ExactSizeAsyncIterator: An async iterator that knows its exact length. +* Extend: Extends a collection with the contents of an async iterator. +* FromAsyncIterator: Conversion from a AsyncIterator. +* FusedAsyncIterator: An async iterator that always continues to yield None when exhausted. +* IntoAsyncIterator: Conversion into a AsyncIterator. +* Product: Trait to represent types that can be created by multiplying the elements of an async iterator. +* AsyncIterator: An asynchronous stream of values. +* Sum: Trait to represent types that can be created by summing up an async iterator. As detailed in previous sections, the migrations to add these traits are out of scope for this RFC. ## Async iteration syntax -Currently, if someone wishes to iterate over a `Stream` as defined in the `futures` crate, +Currently, if someone wishes to iterate over a `AsyncIterator` as defined in the `futures` crate, they are not able to use `for` loops, they must use `while let` and `next/try_next` instead. -We may wish to extend the `for` loop so that it works over streams as well. +We may wish to extend the `for` loop so that it works over async iterators as well. ```rust #[async] -for elem in stream { ... } +for elem in iter { ... } ``` One of the complications of using `while let` syntax is the need to pin. -A `for` loop syntax that takes ownership of the stream would be able to +A `for` loop syntax that takes ownership of the async iterator would be able to do the pinning for you. We may not want to make sequential processing "too easy" without also enabling parallel/concurrent processing, which people frequently want. One challenge is that parallel processing wouldn't naively permit early returns and other complex -control flow. We could add a `par_stream()` method, similar to +control flow. We could add a `par_async_iter()` method, similar to [Rayon's](https://github.com/rayon-rs/rayon) `par_iter()`. Designing this extension is out of scope for this RFC. However, it could be prototyped using procedural macros today. -## "Lending" streams +## "Lending" async iterators -There has been much discussion around lending streams (also referred to as attached streams). +There has been much discussion around lending async iterators (also referred to as attached async iterators). ### Definitions [Source](https://smallcultfollowing.com/babysteps/blog/2019/12/10/async-interview-2-cramertj-part-2/#the-need-for-streaming-streams-and-iterators) -In a **lending** stream (also known as an "attached" stream), the `Item` that gets -returned by `Stream` may be borrowed from `self`. It can only be used as long as +In a **lending** async iterator (also known as an "attached" async iterator), the `Item` that gets +returned by `AsyncIterator` may be borrowed from `self`. It can only be used as long as the `self` reference remains live. -In a **non-lending** stream (also known as a "detached" stream), the `Item` that -gets returned by `Stream` is "detached" from self. This means it can be stored +In a **non-lending** async iterator (also known as a "detached" async iterator), the `Item` that +gets returned by `AsyncIterator` is "detached" from self. This means it can be stored and moved about independently from `self`. -This RFC does not cover the addition of lending streams (streams as implemented through -this RFC are all non-lending streams). Lending streams depend on [Generic Associated Types](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html), which are not (at the time of this RFC) stable. +This RFC does not cover the addition of lending async iterators (async iterators as implemented through +this RFC are all non-lending async iterators). Lending async iterators depend on [Generic Associated Types](https://rust-lang.github.io/rfcs/1598-generic_associated_types.html), which are not (at the time of this RFC) stable. -We can add the `Stream` trait to the standard library now and delay -adding in this distinction between the two types of streams - lending and -non-lending. The advantage of this is it would allow us to copy the `Stream` +We can add the `AsyncIterator` trait to the standard library now and delay +adding in this distinction between the two types of async iterators - lending and +non-lending. The advantage of this is it would allow us to copy the `AsyncIterator` trait from `futures` largely 'as is'. -The disadvantage of this is functions that consume streams would -first be written to work with `Stream`, and then potentially have -to be rewritten later to work with `LendingStream`s. +The disadvantage of this is functions that consume async iterators would +first be written to work with `AsyncIterator`, and then potentially have +to be rewritten later to work with `LendingAsyncIterator`s. -### Current Stream Trait +### Current AsyncIterator Trait ```rust -pub trait Stream { +pub trait AsyncIterator { type Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; @@ -603,10 +604,10 @@ pub trait Stream { This trait, like `Iterator`, always gives ownership of each item back to its caller. This offers flexibility - such as the ability to spawn off futures processing each item in parallel. -### Potential Lending Stream Trait +### Potential Lending AsyncIterator Trait ```rust -trait LendingStream<'s> { +trait LendingAsyncIterator<'s> { type Item<'a> where 's: 'a; fn poll_next<'a>( @@ -615,9 +616,9 @@ trait LendingStream<'s> { ) -> Poll>>; } -impl LendingStream for S +impl LendingAsyncIterator for S where - S: Stream, + S: AsyncIterator, { type Item<'_> = S::Item; @@ -625,59 +626,59 @@ where self: Pin<&'s mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - Stream::poll_next(self, cx) + AsyncIterator::poll_next(self, cx) } } ``` -This is a "conversion" trait such that anything which implements `Stream` can also implement -`LendingStream`. +This is a "conversion" trait such that anything which implements `AsyncIterator` can also implement +`LendingAsyncIterator`. This trait captures the case where we re-use internal buffers. This would be less flexible for -consumers, but potentially more efficient. Types could implement the `LendingStream` -where they need to re-use an internal buffer and `Stream` if they do not. There is room for both. +consumers, but potentially more efficient. Types could implement the `LendingAsyncIterator` +where they need to re-use an internal buffer and `AsyncIterator` if they do not. There is room for both. We would also need to pursue the same design for iterators - whether through adding two traits or one new trait with a "conversion" from the old trait. This also brings up the question of whether we should allow conversion in the opposite way - if -every non-lending stream can become a lending one, should _some_ lending streams be able to +every non-lending async iterator can become a lending one, should _some_ lending async iterators be able to become non-lending ones? **Coherence** The impl above has a problem. As the Rust language stands today, we cannot cleanly convert -impl Stream to impl LendingStream due to a coherence conflict. +impl AsyncIterator to impl LendingAsyncIterator due to a coherence conflict. If you have other impls like: ```rust -impl Stream for Box where T: Stream +impl AsyncIterator for Box where T: AsyncIterator ``` and ```rust -impl LendingStream for Box where T: LendingStream +impl LendingAsyncIterator for Box where T: LendingAsyncIterator ``` -There is a coherence conflict for `Box`, so presumably it will fail the coherence rules. +There is a coherence conflict for `Box`, so presumably it will fail the coherence rules. [More examples are available here](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a667a7560f8dc97ab82a780e27dfc9eb). Resolving this would require either an explicit “wrapper” step or else some form of language extension. -It should be noted that the same applies to Iterator, it is not unique to Stream. +It should be noted that the same applies to Iterator, it is not unique to AsyncIterator. We may eventually want a super trait relationship available in the Rust language ```rust -trait Stream: LendingStream +trait AsyncIterator: LendingAsyncIterator ``` This would allow us to leverage `default impl`. -These use cases for lending/non-lending streams need more thought, which is part of the reason it +These use cases for lending/non-lending async iterators need more thought, which is part of the reason it is out of the scope of this particular RFC. ## Generator syntax @@ -686,10 +687,10 @@ is out of the scope of this particular RFC. In the future, we may wish to introduce a new form of function - `gen fn` in iterators and `async gen fn` in async code that can contain `yield` statements. Calling such a function would -yield a `impl Iterator` or `impl Stream`, for sync and async -respectively. Given an "attached" or "borrowed" stream, the generator +yield a `impl Iterator` or `impl AsyncIterator`, for sync and async +respectively. Given an "attached" or "borrowed" async iterator, the generator could yield references to local variables. Given a "detached" -or "owned" stream, the generator could yield owned values +or "owned" async iterator, the generator could yield owned values or things that were borrowed from its caller. ### In Iterators @@ -715,16 +716,16 @@ async gen fn foo() -> Value After desugaring would result in a function like: ```rust -fn foo() -> impl Stream +fn foo() -> impl AsyncIterator ``` -If we introduce `-> impl Stream` first, we will have to permit `LendingStream` in the future. -Additionally, if we introduce `LendingStream` later, we'll have to figure out how -to convert a `LendingStream` into a `Stream` seamlessly. +If we introduce `-> impl AsyncIterator` first, we will have to permit `LendingAsyncIterator` in the future. +Additionally, if we introduce `LendingAsyncIterator` later, we'll have to figure out how +to convert a `LendingAsyncIterator` into a `AsyncIterator` seamlessly. ### Differences between Iterator generators and Async generators -We want `Stream` and `Iterator` to work as analogously as possible, including when used with generators. However, in the current design, there are some crucial differences between the two. +We want `AsyncIterator` and `Iterator` to work as analogously as possible, including when used with generators. However, in the current design, there are some crucial differences between the two. Consider Iterator's core `next` method: @@ -744,7 +745,7 @@ The general shape will be: gen_fn().pin_somehow().adapter1().adapter2() ``` -With streams, the core interface _is_ pinned, so pinning occurs at the last moment. +With async iterators, the core interface _is_ pinned, so pinning occurs at the last moment. The general shape would be @@ -752,7 +753,7 @@ The general shape would be async_gen_fn().adapter1().adapter2().pin_somehow() ``` -Pinning at the end, like with a stream, lets you build and return those adapters and then apply pinning at the end. This may be the more efficient setup and implies that, in order to have a `gen fn` that produces iterators, we will need to potentially disallow borrowing yields or implement some kind of `PinnedIterator` trait that can be "adapted" into an iterator by pinning. +Pinning at the end, like with an async iterator, lets you build and return those adapters and then apply pinning at the end. This may be the more efficient setup and implies that, in order to have a `gen fn` that produces iterators, we will need to potentially disallow borrowing yields or implement some kind of `PinnedIterator` trait that can be "adapted" into an iterator by pinning. For example: @@ -770,7 +771,7 @@ default impl PinIterator for T { .. } Pinning also applies to the design of AsyncRead/AsyncWrite, which currently uses Pin even through there is no clear plan to make them implemented with generator type syntax. The asyncification of a signature is currently understood as pinned receiver + context arg + return poll. -Another key difference between `Iterators` and `Streams` is that futures are ultimately passed to some executor API like spawn which expects a `'static` future. To achieve that, the futures contain all the state they need and references are internal to that state. Iterators are almost never required to be `'static` by the APIs that consume them. +Another key difference between `Iterators` and `AsyncIterators` is that futures are ultimately passed to some executor API like spawn which expects a `'static` future. To achieve that, the futures contain all the state they need and references are internal to that state. Iterators are almost never required to be `'static` by the APIs that consume them. It is, admittedly, somewhat confusing to have Async generators require Pinning and Iterator generators to not require pinning, users may feel they are creating code in an unnatural way when using the Async generators. This will need to be discussed more when generators are proposed in the future.