7

As far as I know, in C programming language(and many C - based languages), when an arithmetic operation overflows over an N-bit integer, this overflow shortens the result to modulo N-th power of 2, retaining only the LSB's of the resultant.

What happens when such an integer arithmetic operation overflows in Rust programming language.

Hari Krishnan U
  • 166
  • 5
  • 16
  • 3
    The first sentence of your question isn't the full picture. Signed integer overflow in C is undefined behaviour, and unsigned overflow is modulo the max value of the type (which is not necessarily the same as what you claim, due to things like padding bits being permitted). – kopecs Aug 16 '21 at 17:41
  • Heavily related: https://stackoverflow.com/questions/60238060/is-signed-integer-overflow-in-safe-rust-in-release-mode-considered-as-undefined – E_net4 Aug 17 '21 at 09:45

2 Answers2

10

When you’re compiling in debug mode, Rust includes checks for integer overflow that cause your program to panic at runtime if this behavior occurs.

When you’re compiling in release mode with the --release flag, Rust does not include checks for integer overflow that cause panics. For signed values the result is based on two's complement wrapping. For unsigned values the result is modulo the maximum value of the type + 1.

The standard library has a number of functions for more control over what should happen in an overflow case, e.g., checked_add, wrapping_add, and some other variations.

If you haven't already, I suggest reading The Rust Programming Language, which has a section addressing this specifically.

kopecs
  • 1,545
  • 10
  • 20
  • Out of curiosity: isn’t inserting runtime checks at every single arithmetic operation super costly, both in program size and in running time? (for running time, I guess you benefit from branch prediction heavily, but still) – Maëlan Jul 04 '22 at 16:18
  • It certainly has a runtime cost (which is one of the reasons other than optimisations why Rust debug builds are generally quite comparatively slower than C -O0 builds). While there's some overhead for program size I wouldn't say its too much of a concern given the size is also heavily driven by debug symbols. Of course, if you want to avoid this you can just build a release binary. – kopecs Jul 05 '22 at 16:54
6

From the Rust Reference, chapter "Behavior not considered unsafe":

Integer oveflow

If a program contains arithmetic overflow, the programmer has made an error. In the following discussion, we maintain a distinction between arithmetic overflow and wrapping arithmetic. The first is erroneous, while the second is intentional.

When the programmer has enabled debug_assert! assertions (for example, by enabling a non-optimized build), implementations must insert dynamic checks that panic on overflow. Other kinds of builds may result in panics or silently wrapped values on overflow, at the implementation's discretion.

In the case of implicitly-wrapped overflow, implementations must provide well-defined (even if still considered erroneous) results by using two's complement overflow conventions.

The integral types provide inherent methods to allow programmers explicitly to perform wrapping arithmetic. For example, i32::wrapping_add provides two's complement, wrapping addition.

The standard library also provides a Wrapping<T> newtype which ensures all standard arithmetic operations for T have wrapping semantics.

See RFC 560 for error conditions, rationale, and more details about integer overflow.

So, depending on your compiler settings, your program may panic or silently wrap when overflowing. Depending on either of those behaviors is not a good idea. If you need to handle the possibility of overflow, use either wrapping functions to explicitely tell the compiler that you want and expect overflows to wrap back, or use checked methods like u32::checked_add to handle overflow manually.

Elias Holzmann
  • 3,216
  • 2
  • 17
  • 33