Explicit underflow allowed in match


#1

In the code below, when matching an usize, the compiler allows for explicit underflow of said usize with no compiler error. As you can see this underflow is very explicit and the compiler should be able to notice this. Explicit overflows are also allowed by the compiler.

What is the “supposed” behaviour (or undefined behaviour) for this underflow and what kind of runtime error should I expect if n as usize = 0?

I’m just curious of what happens in this case as I’m not an expert on Rust. If there’s some documentation of this “feature”, and already open issue or a RFC on this, please link to it, as I haven’t found anything on it.

fn my_function(n: MyEnum) {
    let (n1, n2) = match n as usize {
        0..8 => (3, (n-1)*4),
        _    => (4, (n-8)*4),
    }
}

#2

When I run it I get

thread 'main' panicked at 'attempt to subtract with overflow', src/main.rs:3:22

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=df99d22e26109ef7c959ab0bcd52fba4

What were you expecting it to do instead?


#3

I think that @spcan was referring to how there wasn’t any kind of static compile-time checking, not runtime underflow checks like what you show. They want it to simply not compile or give a warning, not panic at runtime.


#4

Overflow and underflow will panic in debug builds, and will use two’s-complement wrapping in release builds. They never trigger undefined behavior, but it’s considered a program error to rely on wrapping; Rust code is expected to work correctly when overflow checking is enabled. See the book for some more details and RFC 560 for some old historical context.

The compiler can warn about overflow and underflow in some very specific, limited cases, but these compile-time checks aren’t (and can’t be) exhaustive.


#5

I don’t see what’s “explicit” about the overflow in your example. Just regular old arithmetic, which may or may not happen to overflow.

Detecting overflow at compile time is, in general, intractable. You can do it for certain cases (and Rust does), but detecting it in general is equivalent to the halting problem.

Could the compiler do more? Sure. But there will always be gaps.

Pedantry: Underflow is something that happens to floating-point numbers. Usually people call integer overflow overflow regardless of whether it’s at the top or bottom end of the range. Or just call it wraparound.


#6

The compiler cannot reject my_function because it’s possible that n will always be big enough, in which case the code will work correctly. A function doesn’t have to work on any possible value of its argument.


#7

I think we should perform some Value Range Analysis in rustc front-end, so the compiler can both spot some wrong code statically, and it can allow some more cases of panic-less integral conversions.


#8

@trentj With explicit I meant for a human. Seeing as the range in the match accepts 0 and then subtracts from it, it is quite obvious. What I was curious about is how obvious it is to the compiler and if there was any RFC for this kind of checks.
@mbrubeck thanks for the RFC link, really useful and interesting.


#9

I think this is more suitable as a clippy lint, rather than a compiler warning. This kind of lint would certainly be useful.


#10

Having it as a Clippy lint isn’t enough because I’d like that feature to interact
with casting.