You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: rfc-drafts/stream.md
+128-6
Original file line number
Diff line number
Diff line change
@@ -73,10 +73,9 @@ pub trait Stream {
73
73
fnnext(&mutself) ->Next<'_, Self>
74
74
where
75
75
Self:Unpin;
76
+
{ ... }
76
77
}
77
78
```
78
-
* For information on why `Self: Unpin` is included in the `next`
79
-
method, please see [this discussion on the draft RFC](https://github.com/rust-lang/wg-async-foundations/pull/15#discussion_r452482084).
80
79
81
80
The arguments to `poll_next` match that of the [`Future::poll`] method:
82
81
@@ -183,6 +182,43 @@ while let Some(v) = stream.next().await {
183
182
}
184
183
```
185
184
185
+
We could also consider adding a try_next? function (similar to the one in the [futures-rs](https://docs.rs/futures/0.3.5/futures/stream/trait.TryStreamExt.html#method.try_next) crate, allowing
186
+
a user to write:
187
+
188
+
```rust
189
+
whileletSome(x) =s.try_next().await?
190
+
```
191
+
192
+
Adding the `try_next` method is out of the scope of this RFC to keep us focused
193
+
on adding the critical methods needed for async streams first, then adding in
194
+
additional ones at a later date.
195
+
196
+
One thing to note, if a user is using an older version of `futures-util`,
197
+
they would experience ambiguity when trying to use the `next` method that
198
+
is added to the standard library (and redirected to from `futures-core`).
199
+
200
+
This can be done as a non-breaking change, but would require everyone to
201
+
upgrade rustc. We will want to create a transition plan on what this
202
+
means for users and pick the timing carefully.
203
+
204
+
### Why does next require Self:Unpin?
205
+
206
+
When drafting this RFC, there was a [good deal of discussion](https://github.com/rust-lang/wg-async-foundations/pull/15#discussion_r452482084) around why the `next` method requires `Self:Unpin`.
207
+
208
+
To understand this, it helps to take a closer look at the definition of `Next` (this struct is further discussed later in this RFC) in the [futures-util crate](https://docs.rs/futures-util/0.3.5/src/futures_util/stream/stream/next.rs.html#10-12).
209
+
210
+
```rust
211
+
pubstructNext<'a, St:?Sized> {
212
+
stream:&'amutSt,
213
+
}
214
+
```
215
+
Since `Stream::poll_next` takes a pinned reference, the next future needs `S` to be `Unpin` in order to safely construct a `Pin<&mut S>` from a `&mut S`.
216
+
217
+
An alternative approach we could take would be to have the `next` method take `Pin<&mut S>`, rather than `&mut S`. However, this would require pinning even when the type is `Unpin`. The current approach requires pinning only when the type is not `Unpin`. Additionally, if you already have a `Pin<&mut S>`, you can access `.next()` through the implementation described below because `Pin<P>: Unpin`.
218
+
219
+
At the moment, we do not see many `!Unpin` streams in practice (though there is one in the [futures-intrusive crate](https://github.com/Matthias247/futures-intrusive/blob/master/src/channel/mpmc.rs#L565-L625)). Where they will become important is when we introduce async generators, as discussed in [Future possibilities](future-possibilities).
@@ -220,7 +256,7 @@ Stream` values without the need to monomorphize the functions that work
220
256
with them.
221
257
222
258
Unfortunately, the use of poll does mean that it is harder to write
223
-
stream implementations. The long-term fix for this, discussed in the [Future possibilities][future-possibilities] section, is dedicated [generator syntax].
259
+
stream implementations. The long-term fix for this, discussed in the [Future possiblilities](future-possibilities) section, is dedicated [generator syntax].
224
260
225
261
# Drawbacks
226
262
[drawbacks]: #drawbacks
@@ -275,9 +311,6 @@ and to come back to the problem of extending it with combinators.
Wewant `Stream` and `Iterator` toworkasanalogouslyaspossible, includingwhenusedwithgenerators.However, inthecurrentdesign, thereisacrucialdifferencebetweenthetwo.
691
+
692
+
ConsiderIterator'score `next` method:
693
+
694
+
```rust
695
+
pubtraitIterator {
696
+
typeItem;
697
+
698
+
fnnext(&mutself) ->Option<Self::Item>;
699
+
}
700
+
```
701
+
And then compare it to the proposed Stream `next` method:
702
+
703
+
```rust
704
+
pubtraitStream {
705
+
typeItem;
706
+
707
+
fnnext(&mutself) ->Next<'_, Self>
708
+
where
709
+
Self:Unpin;
710
+
}
711
+
```
712
+
713
+
Iterator does not require pinning its core next method. In order for a `gen fn` to operate with the Iterator ecosystem, there must be some kind of initial pinning step that converts its result into an iterator. This will be tricky, since you can't return a pinned value except by boxing.
714
+
715
+
The general shape will be:
716
+
717
+
```rust
718
+
gen_fn().pin_somehow().adapter1().adapter2()
719
+
```
720
+
721
+
With streams, the core interface _is_ pinned, so pinning occurs at the last moment.
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.
730
+
731
+
For example:
732
+
733
+
```rust
734
+
traitPinIterator {
735
+
typeItem;
736
+
fnnext(self:Pin<&mutSelf>) ->Self::Item;
737
+
// combinators can go here (duplicating Iterator for the most part)
// this would be nice.. but would lead to name resolution ambiguity for our combinators 😬
745
+
defaultimpl<T:Iterator> PinIteratorforT { .. }
746
+
```
747
+
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.
748
+
749
+
### Yielding
750
+
751
+
It would be useful to be able to yield from inside a for loop, as long as the for loop is
752
+
over a borrowed input and not something owned by the stack frame.
753
+
754
+
In the spirit of experimentation, boats has written the [propane](https://github.com/withoutboats/propane)
755
+
crate. This crate includes a `#[propane] fn` that changes the function signature
756
+
to return `impl Iterator` and lets you `yield`. The non-async version uses
757
+
`static generator` that is currently in nightly only.
758
+
637
759
Further designing generator functions is out of the scope of this RFC.
0 commit comments