Rust: Ambiguous type casting behaviour when an overflow occurs in a range; to avoid this warn(overflowing_literals) should actually be an error

Created on 13 Jul 2017  路  7Comments  路  Source: rust-lang/rust

I tried this code:

[play]

fn main() {
    let mut n: u8;

    for i in 0..256 {
        n = i;

        println!("{}:{}", n, i);
    }
}

I expected to see this happen:

Output:

0:0
1:1
...
254:254
255:255

Instead, this happened:

Output:

Nothing

Additionally, using as u8 when performing the assignment makes it work correctly:
[play]

fn main() {
    let mut n: u8;

    for i in 0..256 {
        n = i as u8;

        println!("{}:{}", n, i);
    }
}

Output:

0:0
1:1
...
254:254
255:255

This doesn't make sense to me because the range is exclusive for the end parameter, so for i in 0..256 would never overflow a u8.


Meta

rustc --version --verbose:

rustc 1.18.0 (03fc9d622 2017-06-06)
binary: rustc
commit-hash: 03fc9d622e0ea26a3d37f5ab030737fcca6928b9
commit-date: 2017-06-06
host: x86_64-unknown-linux-gnu
release: 1.18.0
LLVM version: 3.9

Backtrace:
None

A-diagnostics C-enhancement

Most helpful comment

But it's a warning, not an error (I believe this is for obscure historical reasons),

Perhaps it's a good moment to try to make it an error again?

All 7 comments

This is all expected behavior, if a bit surprising.

In the first case, you made the assignment n = i; and n was declared as u8, which means i was also inferred to be u8. This made the type of the range bounds also u8 so you got the warning. But it's a warning, not an error (I believe this is for obscure historical reasons), and 256u8 == 0, so your loop is over the range 1..0, which is an empty range.

In the second case, you made the assignment n = i as u8;, which un-constrains i and the literals, so they revert to the default for integers, which is i32, and there's no overflow.

Also note that clippy detects this and issues a warning that the range is empty.

But it's a warning, not an error (I believe this is for obscure historical reasons),

Perhaps it's a good moment to try to make it an error again?

The main reason why I really don't like this behavior, is because the range is exclusive. So someone trying to use 0 through 255 as a u8 in a loop would think to just add one to 255 and do 0..256, but they don't get what they want.

I agree that this behavior is suboptimal. Ideally, the compiler would reject this and force type annotations or casts to let the user decide the behavior. There's almost zero chance someone wants to create an empty range here (apart from testing empty range iteration behavior itself).

I've edited the title now that I understand what's really happening here, but I've left it open since I think that this behaviour has room for improvement.

Current output:

error: range endpoint is out of range for `u8`
 --> src/main.rs:4:14
  |
4 |     for i in 0..256 {
  |              ^^^^^^ help: use an inclusive range instead: `0..=255`
  |
  = note: #[deny(overflowing_literals)] on by default
Was this page helpful?
0 / 5 - 0 ratings