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

@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
Copy link
Contributor

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.

@Gankra
Copy link
Contributor

Gankra 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
Copy link
Contributor

@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.

bors added a commit to rust-lang-ci/rust that referenced this issue Feb 13, 2023
fix: Fix assoc item search finding unrelated definitions

Fixes rust-lang/rust-analyzer#14014
flip1995 pushed a commit to flip1995/rust that referenced this issue Mar 20, 2025
)

Closes rust-lang#14020
Closes rust-lang#14290
Closes rust-lang#14091

Add checks for unstable const traits.

changelog: [`missing_const_for_fn`] fix FP on unstable const traits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants