Add async_stream to create streams via generators#1548
Add async_stream to create streams via generators#1548taiki-e wants to merge 15 commits intorust-lang:masterfrom
Conversation
futures-util/src/async_stream/mod.rs
Outdated
| _phantom: PhantomData<U>, | ||
| // We rely on the fact that async/await streams are immovable in order to create | ||
| // self-referential borrows in the underlying generator. | ||
| _pin: PhantomPinned, |
There was a problem hiding this comment.
Damn, that's my bad, I should have removed the explicit !Unpin implementation as part of rust-lang/rust#55704 (or transformed it into an explicit conditional implementation).
futures-util/src/async_stream/mod.rs
Outdated
| _phantom: PhantomData<U>, | ||
| // We rely on the fact that async/await streams are immovable in order to create | ||
| // self-referential borrows in the underlying generator. | ||
| _pin: PhantomPinned, |
There was a problem hiding this comment.
You shouldn't need this, T: ?Unpin so Self: ?Unpin via the structural rules (although because of PhantomData<U> you might be getting the Unpinness of U as well), if the generator transform ever allows non-self-referential static-marked generators to implement Unpin automatically then this could potentially generate impl Stream + Unpin.
|
One potential future addition to this would be |
|
FWIW, here is something that would likely benefit from |
|
(somewhat OT: @Ekleog it took a few upgrades to my |
c7288aa to
aca5bc0
Compare
futures-async-macro/README.md
Outdated
|
|
||
| // Returns a stream of i32 | ||
| #[async_stream] | ||
| fn foo(stream: impl Stream<Item = String>) -> i32 { |
There was a problem hiding this comment.
One of the concerns with current implementations is that there is an Item type at the "return" position. In the futures-await's implementation, this is specified in the form of an attribute argument #[async_stream(item = i32)].
There was a problem hiding this comment.
@taiki-e that approach seems reasonable to me; are there any reasons why we can't / shouldn't follow future-await's approach?
There was a problem hiding this comment.
@yoshuawuyts There is no particular reason. Also, it is easy to change the implementation to that.
(Because the stream has no final return value, the return position is always empty. The current implementation is just trying to reuse this blank.)
There was a problem hiding this comment.
In futures-await we can use ? operator inside stream. If I understand correctly, with new syntax it's impossible. Is it true?
/// Fetch all provided urls one at a time
#[async_stream(item = String)]
fn fetch_all(client: hyper::Client, urls: Vec<&'static str>) -> io::Result<()> {
for url in urls {
let s = await!(fetch(client, url))?;
stream_yield!(s);
}
Ok(())
}There was a problem hiding this comment.
@kgv yes, because of streams no longer being required to have an error and item type that no longer works.
You can probably do something mostly equivalent with a try block though:
/// Fetch all provided urls one at a time
#[async_stream(item = io::Result<String>)]
fn fetch_all(client: hyper::Client, urls: Vec<&'static str>) {
let res = try {
for url in urls {
yield Ok(fetch(client, url).await?);
}
};
if let Err(err) = res {
yield Err(err);
}
}and it may be possible to add more sugar for this in the future (an async_try_stream maybe?).
There was a problem hiding this comment.
and it may be possible to add more sugar for this in the future (an
async_try_streammaybe?).
I will try it after this PR is merged. And I think it will be something like the following:
#[async_try_stream(ok = String, error = io::Error)]
fn fetch_all(client: hyper::Client, urls: Vec<&'static str>) {
for url in urls {
yield fetch(client, url).await?;
}
}
// desugaring into
fn fetch_all(client: hyper::Client, urls: Vec<&'static str>) -> impl Stream<Item = Result<String, io::Error>> {
from_generator(static move || { // impl Generator<Yield = String, Return = Result<(), io::Error>>
for url in urls {
yield Poll::Ready(fetch(client, url).await?);
}
return Ok(()); // It is the same behavior as try_blocks that this is added automatically.
loop { yield Poll::Pending }
})
}There was a problem hiding this comment.
(Also, if we add async_try_stream, it may be preferable to write Item at the position of the attribute argument, not at the position of return, so I will revert it)
3ecd7bc to
7db16a1
Compare
9e3d63d to
4f59a41
Compare
4f12883 to
d45caae
Compare
|
I really appreciate this feature and would be happy to help with anything. I haven't been working with futures 0.3 for too long but am very willing to help. |
|
@cramertj Any thoughts about this pr? |
de11312 to
7458bcd
Compare
|
Published as futures-async-stream -- Thanks everybody for your patience. It may not be what was expected, but I believe it would be better to polish it as a separate project and then decide if it should eventually merge. |
This adds a proc-macro crate for experimenting with the async stream API that we may introduce as a feature of language in the future.
The implementation is basically similar tofutures-await's#[async] forand#[async_stream]. (re-added some of the files deleted by #1050 and updated it.)EDIT: After introducing
.awaitsyntax, it was not possible to keep the same implementation in stream scope (#[async_stream]) and future scope (async fn), so it was necessary to adjust it.cc alexcrichton/futures-await#111
Rendered