My probably wrong understanding is that the philosophy behind Rust is that code without IO/unsafe shall not panic. It might provide useless results but it won't panic. But dividing an integer by zero causes a panic (if I recall).
Edit: derp. Of course they can panic. You really can't guard against overflow or OOM and such. gpm helped correct my thinking: it's not about avoiding panics, it's about avoiding undefined behaviour, forcing you to define all possible branching paths.
What safe code isn't allowed to result in is undefined behavior. Panics are defined, they just happen to be defined to print an error message and abort the program (by default, slightly simplified version of how they work).
By the way: If you want to handle overflows you can use methods like `checked_add`, or you could do something like the suggestion for division by zero and create a wrapper type that always does `CheckedI32 + CheckedI32 -> Option<CheckedI32>` (or something). I might implement `Option<CheckedI32> + Option<CheckedI32> -> Option<CheckedI32>` as well so you can delay checking for overflow/division by zero until you want to use the result of the arithmetic.
What I am reading from this is that in Rust, debug assertions cause a different semantics. That does not sound right at all.
For example, if you do "u32::INTEGER_MAX + 3" in debug, it will panic, but in production code it will not (and will use the underlying CPU's result of "2")
In most scenarios, this is not actually what you want. If your program has the possibility of doing arithmetic near or past the bounds, you should define the semantics you need.
I.e., you can use one of saturating_add, wrapping_add, checked_add, or overflowing_add, depending on the semantics that are correct for your application. All of these behave the same between dev and prod.
Assertions, check if a condition is true, and if it is false print an error message and exit the program. Assertions in rust are written `assert!(expr, optional_formatting_stuff)`.
Debug assertions do the same, but they are only executed when the `debug_assertions` flag is passed to the compiler, typically for performance reasons. In C terms, a debug assert is basically `#ifdef DEBUG_ASSERRTIONS assert(foo) #endif`. Debug assertiosn in rust are written `debug_assert!(expression, optional_formatting_stuff)`.
"Formally", arithmetic overflow in rust is defined to either wrap (as per 2s complement arithmetic) or panic.
In todays compilers that means they perform 2s complement artihmetic when debug assertions are turned off, and they panic when it is turned on. If performance of checking for overflows improves in the future, it will be considered an acceptable (backwards compatible) change to always panic instead, but that would have too large a performance impact today.
For x86, you would get an exception. Of course the compiler could insert a check for 0 and a branch, avoiding the exception.
For PowerPC and I believe most RISC, there is no exception. Most processors would give a specific value, such as 0x80000000 for 32-bit. Of course the compiler could insert a check for 0 and a branch, intentionally jumping to code that raises an exception.
On MIPS the convention was exactly that. The compiler would add that extra code so that you'd get an exception even though the hardware wouldn't give one.
This often causes RISC processor emulators compiled for x86 to crash.
It's one of the many architecture-specific things that can poke through the abstraction layers to burn people. Another common group of them, for which I'm curious what rust does, is shift values that are out of range or that shift negative values right. For example: -6 shifted right by 1, 42 shifted left by 4096, 12 shifted right by -2, 88 shifted left by -3, -13 shifted right by 256, etc. (and for the non-negative values, does it matter if they are an unsigned type or not?)
https://play.rust-lang.org/?version=stable&mode=debug&editio...
error: attempt to divide by zero
--> main.rs:2:16
|
2 | let result = 1 / 0;
| ^^^^^
|
= note: #[deny(const_err)] on by default
error: this expression will panic at runtime
--> main.rs:2:16
|
2 | let result = 1 / 0;
| ^^^^^ attempt to divide by zero
error: aborting due to 2 previous errors
very simple example though, no idea what happens when that value comes via socket or file, guess that would panic! then.And yep, the result is a panic, even with debug_assertions off (i.e. you make overflows wrap instead of panic).
https://play.rust-lang.org/?version=stable&mode=release&edit...