No they can’t. Overflows aren’t a real problem. Do not add checked_mul to all your maths.
Thankfully Rust changed overflow behavior from “undefined” to “well defined twos-complement”.
No they can’t. Overflows aren’t a real problem. Do not add checked_mul to all your maths.
Thankfully Rust changed overflow behavior from “undefined” to “well defined twos-complement”.
Having done a bunch of formal verification I can say that overflows are probably the most common type of bug by far.
Arithmetic overflows have become the punchline of video game exploits.
Unsigned underflow is also one of the most dangerous types. You go from one of the smallest values to one of the biggest values.
Overflow bugs are a real pain, and so easy to prevent in Rust with just a function call. It's pretty high on my list of favorite improvements over C/C++
Don’t do arithmetic with u8 or probably even u16.
But at the same time in real code in the real world you just do the maths, throw caution to the wind, and if it overflows and produces a bug you just fix it there. It's not worth the performance hit and your fellow developers will call you mad if you try to have a whole codebase with only checked maths.
If I play "Appeal to Authority" you can read some thoughts on this from Alexandrescu, Stroustrup, and Carruth here: https://stackoverflow.com/questions/18795453/why-prefer-sign...
Unsigned integers are appealing because they make a range of invalid values impossible to represent. That's good! Indices can't be negative so simply do not allow negative values.
The issues are numerous, and benefits are marginal. First and foremost it is extremely common to do offset math on indices whereby negative values are perfectly valid. Given two indices idxA and idxB if you have unsigned indices then one of (idxB - idxA) or (idxA - idxB) will underflow and cause catastrophe. (Unless they're the same, of course).
The benefits are marginal because even though unsigned cannot represent a value below the valid range it can represent a value above container.size() so you still need to bounds check the upper range. If you can't go branchless then who cares about eliminating one branch that can always be treated as cold.
On a modern 64-bit machine doing math on smaller integers isn't any faster and may in fact be slower!
Now it can be valuable to store smaller integers. Especially for things like index lists. But in this case you're probably not doing any math so the overflow/underflow issue is somewhat moot.
Anyhow. Use unsigned when doing bitmasking or bitmanipulation. Otherwise default to signed integer. And default to i64/int64_t. You can use smaller integer types and even unsigned. Just use i64 by default and only use something else if you have a particular reason.
I'm kinda rambling and those thoughts are scattered. Hope it was helpful.
Of course don't use saturating_add to calculate account balance, there you should use checked_add.
Similarly in this case, it's not like we don't have languages that do checked arithmetic throughout by default. VB.NET, for example, does exactly that. Higher-level languages have other strategies to deal with the problem; e.g. unbounded integer types as in Python, which simply never overflow. And, like you say, this sort of thing is considered unacceptable for low-level code on perf grounds, but, given the history with nulls and OOB checking, I think there is a lesson here.