-
Notifications
You must be signed in to change notification settings - Fork 321
Added Range iterator #447
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
Added Range iterator #447
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi there. Surprising to see that there is apparently no existing functionality like this. Could polish it a bit and remove the (probably accidental) formatting changes?
One potential reason it doesn't exist is that it can be accomplished with skip-take: If I'm reading things correctly here, It may still be worth keeping the |
This is very similar to |
@jswrenn I don't think it should be called drain, because it consumes the whole iterator, not just a part of a vector, like drain does. But yeah, the formatting changes were accidental, and reusing skip and take may be a better idea. The problem with those though is that they wouldn't work too well with unlimited iterators. |
I feel pretty strongly that it shouldn't be @TrolledWoods One could also use an associated type for the return type so that |
That's a great idea @scottmcm |
Yeah, this is definitely better, now we can have |
I was thinking maybe the range standalone function should use IntoIterator instead of Iterator, since it seems like most other standalone functions do that in itertools |
And do we really want the access to the range function to be via |
src/range.rs
Outdated
/// Limits an iterator to a range. See [`Itertools::range`] | ||
/// for more information. | ||
pub fn range<I, R>(iter: I, range: R) | ||
-> R::IterTo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of exploiting existing iterator implementations. Three questions:
-
Should we introduce a type alias
RangeIterator<R, I>
so that our public interface does not expose that the return type is realized as an associated type? -
I guess at some point users want to write:
fn doit(r: impl std::ops::RangeBounds) { give_me_usize_iterator().range(r); // somewhere within the function }
This - as far as I can see - does not work because
range
expects anIntoRangeIter
, so the user probably goes ahead and tries:fn doit(r: impl IntoRangeIter) { give_me_usize_iterator().range(r); // somewhere within the function }
But if I am not mistaken this will not work either, because
IntoRangeIter
requires to specify an iterator upfront. Is it possible to implementIntoRangeIter
in a way that does not require the user to specify the iterator? -
Should we try to inform the type system that each
IntoRangeIter
also satisfiesstd::ops::RangeBounds
so that users can writefn doit(r: impl IntoRangeIter<(possibly iter type)> { give_me_iter().range(r.clone()); vec![1,2,3,4,5].drain(r); }
without having to specify the (imho opinion) implied
RangeBounds
trait bound?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that the TraitBounds -> IntoRangeIter conversion can work, because IntoRangeIter type is implement specifically for each TraitBounds type, so implementing it for all TraitBounds types would be a conflict. I'm not sure if rust team is working on prioritized implementations, but if it's possible to somehow say "use these implementations, or this other even more generic one if the specific ones are not possible".
IntoIterRange implementing RangeBounds is definitely something we should add.
And for some reason I had the idea that trait methods couldn't have generics, but that doesn't seem to be the case, so we shouldn't need to pass around the iterator information in the trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I'll see if I can figure out that type alias thing. I am not too used to working with some of these things, I have stuck in my little rust box and not gone into other places too much, but it's great that I am now!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking that maybe we should rename "IntoRangeIter" to "IterRangeBounds", because I feel like that makes more sense. The bounds aren't the thing that turn into an iterator after all, they just are a RangeBounds that work on an iterator
I'm going to be honest though, it feels like the associated type version has a few more drawbacks than the previous approach. It adds a new trait, it changes its return type based on the input, it cannot take a generic RangeBounds type, and(giving it some more thought I realized this) you have to pass around the iterator with the range as a type parameter until generic trait types are stable. The current approach is sure a lot easier to implement and less error prone, but I'm not so sure anymore it's the right approach |
The trait approach is very close to how the core library implements slice indexing: it defines a trait, Likewise, I don't think implementing iterator indexing with an “ Here's what an implementation mirroring I'm curious if this is a candidate for standard library inclusion. |
Then, making it a trait seems to be very nicely consistant with the standard library! I have really terrible internet at the moment, so it's kindof hard for me to push changes at the moment. |
One thing though, since range is so closely mapped by slice.get(), maybe it should just be called get, and support usize indexing as well? So .range(4) (or .get(4)), is just the same as .nth(4). This range thing is for convenience to begin with, so I don't think that this addition would be strange |
Agree completely. Both of those changes are already incorporated into the playground URL I linked. :-) |
Welp, I don't know about the naming, but I think it's starting to come together quite nicely now |
Let's stick to mirroring
Could you also just make sure that indentation consistently uses four spaces? Right now it's a mix of tabs and spaces. |
Oh sorry, I prefer tabs for my personal projects, so my editor uses them. I'll rename the things and fix the tabs |
Should I rename the module to iterator_index too, or just keep it as iter_index? |
|
Are there any more tweaks that I should do? |
I could limit the IteratorIndex::Output to be an IntoIterator, which could allow people to write some nice generic code with the get if they wanted to. Another option would be to limit the Output to Iterator, but then the .get(5) case would have to return the Option iterator, which I think could be kinda clunky for users |
Limit The
|
Done! |
I'm excited to merge this, but I want to first be sure we won't eventually be in conflict with the core library. I've opened an issue on @rust-lang/rust asking whether this PR would be a good core library addition: rust-lang/rust#73482 |
Did you consider using edit: I guess you'd need to |
That's a good point. nth isn't consistant with the rest of the things. I think however the better approach is to make |
Another consideration -- slice It's probably more consistent for iterators to be lazy all around. |
I'm of two minds. Approach 1: It's just a shorthandWe could have a If an end-user wants to lazily get the I like the ease of explanation, ease of implementation, and flexibility of this approach. The downside of this approach is
Approach 2: Consistency above allIf we keep the I like the semantic consistency of this approach. However, it's not as easy to implement, and (IMO) it's not as easy to explain. It's a lot of work just for a In the absence of demonstrated demand for a If this issue proves to be major roadblock, let's just remove the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TrolledWoods, could you:
- make the requested changes
rustfmt src/index_iter.rs
- fix the merge conflict
After that, I'd be delighted to merge this!
I'm terribly sorry it took so long, I completely forgot about this and it seems github is not very adamant about telling you new messages have appeared. If there's anything more you need do tell! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jswrenn @phimuemue
Hi everyone, I had a mental draft for this. This seems quite promising and I agree to not include .get(usize)
.
I'm not sure what is the blocker here? Are we waiting for rust-lang/rust#73482 (RFC2845 would fix the painpoint, see #702 (comment))? Or maybe it just got lost in the limbs?! 👻
It would have to be rebased (to run CI) and (thankfully) formatted (for CI to pass).
/// | ||
/// Works similarly to [`slice::get`](https://doc.rust-lang.org/std/primitive.slice.html#method.get). | ||
/// | ||
/// It's a generalisation of [`take`], [`skip`] and [`nth`], and uses these |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[`Iterator::take`], [`Iterator::skip`]
should be enough.
Remove nth
since it is not used anymore.
/// [`take`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.take | ||
/// [`skip`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.skip | ||
/// [`nth`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.nth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove links.
/// Returns an element at a specific location, or returns an iterator | ||
/// over a subsection of the iterator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove "an element at a specific location, or returns".
/// | ||
/// range = vec.iter().get(..).copied().collect(); | ||
/// assert_eq!(range, vec); | ||
/// ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No test about RangeToInclusive
(such as ..=2
) yet, add one or codecov will rightfully complain.
I'd love to see this merged! It just needs a bit of attention and rebasing. |
Nice. @TrolledWoods Are you around to finish this awesome feature of yours? |
I will try to do the changes! Turns out formatting and then un-formatting everything makes it hard for git to merge though, will have to try to clean these commits up somehow... |
Oh, sorry didn't see that it was being worked on already. Nice work! |
@TrolledWoods Thanks for this! I guess this was delayed partially because maintainers were busy elsewhere. |
I thought an iterator that iterates over a range of values would be pretty neat to have. Use if you want to!