rust-itertools / itertools

Extra iterator adaptors, iterator methods, free functions, and macros.
https://docs.rs/itertools/
Apache License 2.0
2.62k stars 298 forks source link

Feature request: Tracked adapter #957

Open advantageous-overtake opened 3 weeks ago

advantageous-overtake commented 3 weeks ago

In my code I have the need to get the most recent index without having to store it every time I advance the CharIndices<'_> iterator, so I propose the following:

A generic implementation for all Iterator<Item = (T, U)>, with the adapter declared as:

struct Tracked<T, U, I>
where
    T: Default,
    I: Sized + Iterator<Item = (T, U)>
{
    iter: I,
    last: T
}

This allows generic integration with common adapters such as Enumerate<_>.

The Default requirement for T could also be dropped and have an Option there if the current design is undesired.

Philippe-Cholet commented 3 weeks ago

@advantageous-overtake I'm not sure I understand, could you elaborate with an usage example?

advantageous-overtake commented 3 weeks ago

Sorry for the delayed answer.

A possible use case would be getting the next byte to-be-yielded by an iterator, rather than having to use tricky ways such as peeking (given that it outputs index and value pairs) for the next index or having to query the length of the iterated buffer if the iterator has been exhausted.

Thank you for your time.

advantageous-overtake commented 3 weeks ago

Basically the adapter would encapsulate the original iterator and simply add a count field, and providing Deref and DerefMut implementations to maintain peeking functionality for types like Peekable<Enumerate<_>>.

advantageous-overtake commented 3 weeks ago

If this is OK. I can start with the implementation straight away.

Philippe-Cholet commented 3 weeks ago

I see you have a clear understanding of it. I'm simply not understanding what it would look like, even what the Tracked<T,U,I> iterator would yield as items? U or (T, U)?

I'd like to know what code you can currently write (probably convoluted/messy) and what beautiful code you could write with Tracked.

advantageous-overtake commented 3 weeks ago

I see you have a clear understanding of it. I'm simply not understanding what it would look like, even what the Tracked<T,U,I> iterator would yield as items? U or (T, U)?

Well, my first thought was that it should be only used on iterators yielding a 2-field tuple, but now I believe that introducing an additional trait going by the name Pair would allow it to be used on any Iterator whose Item: Pair, followed by a blanket Pair implementation for all (T, U).

I'd rather go the Pair trait "way", as it allows for any type to be supported by Tracked and keeps generic bounds clean.

If any name collisions occur due to the trait's name, it could be renamed to Enumerated or alike.

So, in that case would be Tracked<I>.

jswrenn commented 3 weeks ago

If this is OK. I can start with the implementation straight away.

Before you make a PR, please sketch out a quick-and-dirty implementation in the Rust Playground so that we can get a better idea of what you're describing and can see the value it brings.

What @Philippe-Cholet said:

I'd like to know what code you can currently write (probably convoluted/messy) and what beautiful code you could write with Tracked.

...is absolutely necessary.

Looking forward to playing around with this.