Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let the ? operator work natively in try_stream!. #53

Merged
merged 1 commit into from
Jan 21, 2021

Conversation

goffrie
Copy link
Contributor

@goffrie goffrie commented Jan 20, 2021

Insteads of desugaring ? in the macro, we can have the async block
itself return Result<(), E>, and adjust the supporting code so that
? just works. The benefit is that this allows ? operators that are
hidden behind macros.

Insteads of desugaring `?` in the macro, we can have the async block
itself return `Result<(), E>`, and adjust the supporting code so that
`?` just works. The benefit is that this allows `?` operators that are
hidden behind macros.
Copy link
Member

@taiki-e taiki-e left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@taiki-e taiki-e merged commit 623c556 into tokio-rs:master Jan 21, 2021
@taiki-e taiki-e mentioned this pull request Jan 21, 2021
@albel727
Copy link

Is this supposed to break code that compiled before? Because it does for me. Before this PR one could use "return;" in the stream body to stop yielding. Now this fails with

expected type `()`
              found enum `std::result::Result<(), _>`

@albel727
Copy link

Ah yes, now that I read the PR with some coffee, of course it's supposed to break code. Well, simply returning Ok(()) is not a big deal, and the ability to return an Err is more flexible too. But I would really want a v0.3.1 release of async_stream, which lifts the Unpin on Stream::Item requirement, but with this PR included it would probably have to wait for a v0.4.

@goffrie
Copy link
Contributor Author

goffrie commented Mar 17, 2021

Ah, that's a good point. I didn't realize it, but yes this is a breaking change.

I'll leave it to @taiki-e (or other maintainer 🙂) to decide whether to cut a 0.4 or revert this PR.

@taiki-e
Copy link
Member

taiki-e commented Mar 17, 2021

@albel727 good catch!

I tend to accept this at 0.4, but for now, I'd like to revert this. (and I'd like to add more tests before re-land in 0.4.)

@SergioBenitez
Copy link
Contributor

SergioBenitez commented Apr 27, 2021

What is the difference between try_stream! and stream!, besides the expected type of the expr in yield expr? If you take a try_stream! and replace every yield expr with yield Ok(expr), can you simply swap the try_stream! invocation for a stream! invocation?

If the answer is yes, then this leads me to ask: are both stream and try_stream necessary, and should ? just work in stream!? If the answer is no, the docs should be updated to reflect this. Consider that these two functions appears to work identically:

fn f<S>(stream: S) -> impl Stream<Item = io::Result<u8>>
    where S: Stream<Item = io::Result<&'static str>>
{
    try_stream! {
        for await s in stream {
            let num = s?.parse();
            let num = num.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
            yield num;
        }
    }
}

fn g<S>(stream: S) -> impl Stream<Item = io::Result<u8>>
    where S: Stream<Item = io::Result<&'static str>>
{
    stream! {
        for await s in stream {
            let num = s?.parse();
            let num = num.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
            yield Ok(num);
        }
    }
}

@Kestrer
Copy link
Contributor

Kestrer commented Apr 27, 2021

? cannot be used inside stream! because the async block must evaluate to (). We could modify stream! to support evaluating to Result<()> in which case the difference with try_stream! would be that it requires an Ok(()) at the end (ok-wrapping the yields can be done by AsyncStream). However, that wouldn't work well with the Try trait as we'd have overlapping impls of when the async block evaluates to () or T: Try.

If Try was implemented for () I think the two macros could be unified however. I haven't seen any plans for that though.

@SergioBenitez
Copy link
Contributor

? cannot be used inside stream! because the async block must evaluate to ().

I don't understand. My example code above uses ? in stream! and works exactly as the try_stream! version does. Can you post an example where they would work differently?

@Kestrer
Copy link
Contributor

Kestrer commented Apr 28, 2021

Ah, right - it works because ? in stream! expands to a match where the Err branch yields and returns. So yes, I think you're right - other than native ? expansion and Ok-wrapping stream! and try_stream! are identical.

The native ? expansion does have an impact on the public API by the way. Consider this (contrived) example, which doesn't compile:

macro_rules! questionmark {
    ($e:expr) => { $e? };
}
let _ = stream! {
    questionmark!(Err(5));
    yield Ok::<i32, i32>(6);
};

Although this case is so rare there's no point in worrying about it much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants