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

Decrementing range behaviour is very confusing when used with unsigned integers #14020

Closed
pimterry opened this Issue May 7, 2014 · 3 comments

Comments

Projects
None yet
3 participants
@pimterry
Copy link

pimterry commented May 7, 2014

Range behaviour differs significantly and unexpectedly from expected behaviour if the range start is an unsigned integer.

use std::iter;

fn main() {
  let range_start: int = 10;
  for x in iter::range_step(range_start, -1, -1) {
    println!("{}", x);
  }
}

This prints 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0.

use std::iter;

fn main() {
  let range_start: uint = 10;
  for x in iter::range_step(range_start, -1, -1) {
    println!("{}", x);
  }
}

(range_start is a uint, not an int)

This prints '10', and stops. This is not expected.

This appears to be because the type signature for range_step uses the same generic parameter for all three argument. Thus, if the start is a uint then the others are all interpreted as uints, the negative numbers are interpreted as very large ints, the range increments by 2^64-1 up to a max of 2^64-1, rather than decrementing by -1, and the range immediately finishes.

This seems like a relatively common operation, and it'd be nice if the rust compiler could catch the incorrect types in this code, and/or the range_step method could properly handle mixing signed and unsigned parameters to the method.

(N.b. I am relatively new to rust, sorry if there's something obvious I'm missing that invalidates this entirely)

@thestinger

This comment has been minimized.

Copy link
Contributor

thestinger commented May 7, 2014

It's not a decrementing range. The type of range_step is fn range<T: ...>(start: T, end: T, step: T). Since you're passing a uint as the first parameter, the other two parameters also take uint. Your generic literals are inferred as being 1u, and then the negation operator (which exists for unsigned integers) is applied. I think #5477 covers the only issue here already.

@thestinger thestinger closed this May 7, 2014

@Gankro

This comment has been minimized.

Copy link
Contributor

Gankro commented Jun 25, 2014

@thestinger This does expose an interesting problem with the range_step function, though. As far as I'm concerned, the primary usecase is to do reverse iteration (which seems to have been deemed "confusing" for the range function to support), for which you need range_step(,,-1). Thus, all of the arguments have to be signed. However, if I want to iterate over unsigned integers, I can't reasonably always convert them to signed integers. One would reasonably expect to be able to iterate from uint::MAX to uint::MIN, but as it stands, none of the range functions seem to permit this. Or am I mistaken?

Edit: I mean, you can just use range().rev() to accomplish this, but it's odd that that's necessary.

@thestinger

This comment has been minimized.

Copy link
Contributor

thestinger commented Jun 25, 2014

@Gankro: Reverse iteration with a step of 1 can be done with range(n, m).rev() because it's a double-ended iterator and that's also how you iterate in reverse over a slice. The step iterators could eventually be double-ended once there's a proper trait for enumerated types with pred and succ rather than it being a hack based on arithmetic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.