Skip to content

More AsyncIterator helper functions #599

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

Closed
Qelxiros opened this issue Jun 7, 2025 · 5 comments
Closed

More AsyncIterator helper functions #599

Qelxiros opened this issue Jun 7, 2025 · 5 comments
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries I-async-nominated T-libs-api

Comments

@Qelxiros
Copy link

Qelxiros commented Jun 7, 2025

Proposal

Problem statement

Working with AsyncIterators is currently very difficult due to a lack of helper functions (Iterator provides 75 methods, while AsyncIterator provides only size_hint).

Motivating examples or use cases

This block of code (from https://github.com/Kobzol/async-iterator-examples/blob/25e239bd708943f4971366b591a99e918771e030/json-line-parser/src/bin/server.rs#L67-L76)

let mut counter = 0;
    while let Some(msg) = poll_fn(|cx| iter.as_mut().poll_next(cx)).await {
        match msg {
            Message::Ping => {}
            Message::Hello(_, count) => {
                counter += count;
            }
        }
    }
    Ok(counter)

would be absurd to write with a standard Iterator. It would be much more idiomatic and readable to write something like this:

let counter = iter.filter_map(|msg| match msg {
    Message::Ping => None,
    Message::Hello(_, count) => Some(count),
}).sum::<u32>();
Ok(counter)

Depending on which methods are available on Message, it might be desirable to eliminate the match statement in favor of a filter and a map separately. There are a plethora of more readable options due to Iterator's helper functions, and it's a big part of what makes working with Iterators in Rust so enjoyable.

Solution sketch

There are many useful methods on Iterator, but these seem like a reasonable subset with which to start. I included fuse() here because its use case is mentioned in the documentation, which is how I got here. I also added fold() because (at least in my experience) while it's not the most common adapter, it's a very powerful one, and one through which many others are implemented.

Note that the returned structs are not the same as the ones for Iterator and implement AsyncIterator instead.

impl AsyncIterator {
    // Item, poll_next, size_hint

    fn fuse(self) -> Fuse<Self> where Self: Sized;
    async fn fold<B, F>(self, init: B, f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B;
    async fn count(self) -> usize;
    fn map<B, F>(self, f: F) -> Map<Self, F> where Self: Sized, F: FnMut(Self::Item) -> B;
    fn filter<P>(self, predicate: P) -> Filter<Self, P> where Self: Sized, P: FnMut(&Self::Item) -> bool;
    fn filter_map<B, F>(self, f: F) -> FIlterMap<Self, F> where Self: Sized, F: FnMut(Self::Item) -> Option<B>;
}

Alternatives

  • The methods that take closures could take async closures. I'm not sure which way is better, feedback would be great.
  • This change could be postponed until AsyncIterator is stabilized. This seems bad because more people are likely to use it if it's ergonomic.
  • A different subset of Iterator helper functions could be chosen, or new async-specific functions could be designed. I have no strong opinions on this either.

Links and related work

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
@Qelxiros Qelxiros added T-libs-api api-change-proposal A proposal to add or alter unstable APIs in the standard libraries labels Jun 7, 2025
@programmerjake
Copy link
Member

all of those methods need to return Futures or AsyncIterators

@Qelxiros
Copy link
Author

Qelxiros commented Jun 7, 2025

I had in my head that the adapter structs (Fuse, Filter, etc.) would have their own async versions, but I didn't actually write that down. Updated.

@programmerjake
Copy link
Member

you still need fold, count, etc. to return Futures.

@Qelxiros
Copy link
Author

Qelxiros commented Jun 7, 2025

Oh, right. Fixed now. Thanks!

@traviscross
Copy link

In the async working group, and on lang, we have a lot of open and ongoing work to do on the trait (or traits) for what will "carry" the combined async and gen/iter effect. It's going to just be too early for us, right now, to consider topics such as adding helpers to these traits. The concerns mentioned in #594 (comment), about adding helpers to Future, also apply here.

Thanks to the author for making this suggestion. However, I'd suggest closing this one out.

@the8472 the8472 closed this as not planned Won't fix, can't repro, duplicate, stale Jun 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-change-proposal A proposal to add or alter unstable APIs in the standard libraries I-async-nominated T-libs-api
Projects
None yet
Development

No branches or pull requests

5 participants