-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
iterator: Add StepBy::size_hint
method
#24720
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @alexcrichton (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. The way Github handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see CONTRIBUTING.md for more information. |
Sorry, there is this bug: #![feature(step_by)]
fn main () {
let mut range = (0..5).step_by(2);
loop {
println!("{:?}", range.size_hint());
match range.next() {
Some(x) => {
println!("{}", x);
},
None => { break }
}
}
} gives
But is it because |
@critiqjo yeah the range iterators are inclusive on the left but exclusive on the right. Could you add some tests for this change as well? (after fixing the underlying bug) |
I'm sorry about editing the original post to add new content (I assume you replied to the mail). So I'll post the edits here as new comment:
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
if *start <= *end {
Some(((*end - *start) / *by) as usize)
} else {
Some(0)
}
} Shouldn't it be The following code gives #![feature(step_trait)]
use std::iter::Step;
fn main () {
let steps = Step::steps_between(&0, &5, &2);
println!("{:?}", steps);
} |
If |
I'll fix the bug... But I have never written tests. Is it correct to add my test to |
I'd have to do some more investigation to see if this is the right fix or not, but your rationale sounds reasonable! Also yeah that location is fine for some tests. |
Another suggestion would be to have a |
It looks like the |
"Fix" only if the definition of But |
Just to make myself more clear: in the definitions I stated (of Suggestion: rename the current |
Regardless of what the name |
Okay... So shall I change the #[inline]
#[allow(trivial_numeric_casts)]
#[allow(unused_comparisons)]
fn steps_between(start: &$t, end: &$t, by: &$t) -> Option<usize> {
if *by == 0 { return None; }
let mut start = *start;
let mut end = *end;
let mut by = *by;
if by < 0 {
start *= -1;
end *= -1;
by *= -1;
}
if start <= end {
let diff = end - start;
if diff % by > 0 {
Some((diff / by) as usize + 1)
} else {
Some((diff / by) as usize)
}
} else {
Some(0)
}
} The original panics if |
It doesn't necessarily need to be super robust (as it's a somewhat private API), but I would recommend some exhaustive tests to accompany this change. |
I've commited the fix and added tests. Please check... |
I think the current implementation of the |
start *= -1; | ||
end *= -1; | ||
by *= -1; | ||
} |
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.
Was this block just necessary for the %
check below? It looks like otherwise the signs just end up handling themselves.
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.
This is so that, for all valid (*start, *end, *by)
triplet, start < end
and all values would be positive... Not needed for unsigned types, should I split it?
As a meta-point, this may want to coordinate with #24865 as there's likely to be merge conflicts between the two.
This is currently not done because, as you mention, specialization would be needed to optimize the implementation for iterators like |
Maybe we can split so that Range uses a separate method (since it always uses step 1). It's quite important that we get the range iterator right and always computing the exact right size. |
Actually, this takes care of both... Step 1, negative or anything... |
We need to take into account the bug I just fixed in #24865. Try a test case with |
if by < 0 { | ||
start *= -1; | ||
end *= -1; | ||
by *= -1; |
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.
Notice that multiplying with -1 is a potential overflow (-128i8 * -1 => overflow)
Oh! I see... But I don't think we should fix these separately... One fix should take care of unit steps, negative steps, and mutli-steps... |
It's not my call but I'd like my bugfix to be picked into the 1.0 release because it's an embarrasing bug, and then it is better as a separate change. We can then add more features here. |
This strategy almost works: If by < 0, swap begin and end. Compute length as usual (using end.wrapping_sub(start) as usize). Divide by by.abs(). by.abs() fails if by == -MIN for a signed value, so that needs a further if case to cover that. |
I have fixed for most of those corner cases... How does it look? |
I think it looks great. As long as we have lots of test for the corner cases, and those pass, we're fine. The $by == 1 case should optimize nicely and be efficient I think too. |
☔ The latest upstream changes (presumably #24865) made this pull request unmergeable. Please resolve the merge conflicts. |
@alexcrichton if this PR seems to add unnecessary complexity to the code, since the |
Thanks! This looks good to me, could you squash the commits together as well? |
if *start <= *end { | ||
// Note: We assume $t <= isize here | ||
// Use .wrapping_sub and cast to usize to compute the | ||
// difference that may not fit inside the range of isize. |
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.
can you copy this comment and put it on the signed case code above?
Done! Thank you, guys! |
⌛ Testing commit 4863680 with merge be2f26b... |
💔 Test failed - auto-mac-64-opt |
Fixes `Step::steps_between` implementations by integer types to correctly handle `by != 1`.
I'm extremely sorry about that... I didn't know about |
@bors: r+ 2a8fc9b No worries! Thanks again! |
⌛ Testing commit 2a8fc9b with merge 613109d... |
`Iterator::size_hint` can be easily implemented for `StepBy`. #23708
Iterator::size_hint
can be easily implemented forStepBy
.#23708