Not to re-relitigate old discussion, but (as I'm sure you know) it's now more probable that you might overflow your integers and then pass them to unsafe code or external APIs, and there are many other bugs or undesirable behavior that could be killed dead instead of allowing them to behave badly. For a not contrived example, I don't want weird stuff to happen when my semaphore counter overflows, or underflows, I want it to just crash, right there. (My perspective and desires come from a project where I didn't care about "raw" performance that much -- the project being a storage engine where C++ was chosen with the performance concerns in mind being lack of GC and resultant traffic shockwaves, so my preferences lie a bit away from CPU integer operation performance and more towards crashing obviously at the spot of the foul. It's the worst kind of bug when you hang mysteriously in production or to write garbage to disk when the error could be caught, and I had many C++ integer underflow worries at that job.)
I also think overflow errors should be conveniently handleable without putting a try! or match expression on every operation, and so should other errors, but I'll spare you that rant :-)
> Not to re-relitigate old discussion, but (as I'm sure you know) it's now more probable that you might overflow your integers and then pass them to unsafe code
Unsafe code needs to validate its inputs. That's part of the contract of unsafe code. If your unsafe code causes memory safety errors when given MAX_INT as an input, then your unsafe code is dangerously broken regardless of how easy it is for safe code to accidentally create a MAX_INT value.
The relevant contract boundary isn't specifically at the edge of the unsafe block, it's at some other level like the public face of your ADT, inside which your unsafe and technically safe regions of code share the same set of invariants. It's immaterial whether the integer operation that results in overflow happens inside or outside what is technically the unsafe block, it should still be trapped. (Sorry about being unclear in that part of my previous comment.)
Does Rust do saturating arithmetic by default? If not, an integer overflow won't pass INT_MAX, it will pass some other int that might be a valid input to the unsafe code.
Rust does "computer" (silently overflowing) arithmetic by default. Saturating[0] and checked[1] are optionally available but require converting to methods (and some other changes for checked as it returns an Option)
Unsafe code should not have memory safety errors when passed valid input. The point is that while integer overflow in Rust might cause incorrect program behaviour, it's not going to cause undefined behaviour (and therefore e.g. executing user input) the way it does in C.
I don't understand your criticism. Anyone who claims anything is "safe" has to be specific about the definition of "safe", or else they'd be making vague unsubstantiated claims.
Rust is "memory safe" per the commonly used Saraswat definition [1] (not proven yet, but the proof is in progress). This is not something we just made up.
I think I misread you. It's certainly reasonable to say that memory safety will reduce the harm, on average, of integer overflows and that this should affect any cost/benefit calculation for implementing overflow checking. What would have been unreasonable is to imply that the safety implications of integer overflow can't matter much or are somehow out of scope because of Rust memory-safety. No matter how nice and precisely-defined the memory-safety criteria are, they're only part (an important one ofc) of what people reasonably expect in terms of safety improvements from a language which aims to be a better, saner successor to C/C++ and is evangelised as such. "Not a deathtrap like C" may be a much fuzzier standard than "memory safe per the Saraswat definition", but that doesn't change the fact that the latter is just a partial means to the former.
> This is not something we just made up.
I deliberately did not say 'made up' or 'created', but rather 'picked' meaning 'selected'. The fact that the map is pre-existing and commonly-used (and rightly so) does not alter the fact that it is not the terrain.
> What would have been unreasonable is to imply that the
> safety implications of integer overflow can't matter much
> or are somehow out of scope because of Rust memory-safety.
pcwalton is not suggesting this. The Rust devs care about reducing the amount of unsafe code in the wild, which means that they care about checked arithmetic operations. But making the world a safer place first requires displacing C++ where it is used in safety-conscious applications, which means being competitive with C++ in terms of speed. And yes, this is all about defaults, given that Rust already offers checked and saturating arithmetic types. That Rust leans on the side of speed here is a pragmatic choice (and one that is rooted in actual benchmarking, corroborating the 2-6x slowdown mentioned in the article), and is only possible because it doesn't violate memory safety by any accepted definition (see also how Rust gladly accepts the cost of checked array indexing, while also going to great lengths to provide elaborate abstractions (iterators) to eliminate that same cost). It's also an unfortunate accident of our modern historical context: once compilers get better at optimizing overflow checkes, and/or once hardware gains better support for efficient overflow checks, and/or once Rust begins to actually demonstrate a credible ability to replace C++ in safety-conscious applications (and thus doesn't need to beat the drum of performance quite so vigorously), then checked arithmetic by default can be trivially added to Rust after the fact (though it would obviously be a breaking change, necessitating a version bump).
(For completeness, I'd like to point out here that checked arithmetic is not a panacea. For any range that you wish to express in an integral type, it is vanishingly unlikely that INT_MAX is actually the logical upper bound. If I have an RPG where characters can quiver 100 arrows but I forget to enforce the upper bound, checked arithmetic won't save me from the fact that my program can now exist in somewhere between 28 invalid states (signed eight-bit variable) and nine sextillion invalid states (for a signed pointer-sized variable on a 64-bit platform, as is so common for "default" integral types). The real solution is reifying integral bounds in the type system, as I expect the language that someday replaces Rust to do.)
Other ways in which the choice of what constitutes safety is convenient: you can recurse until the stack overflows, and you can make infinite loops instead of having to prove through the type system that everything terminates. These are reasonable design decisions. If allowing silent integer overflow is the wrong choice for Rust, it's barely the wrong choice, because the costs and benefits are clearly arguable and user-specific.
> These are reasonable design decisions. If allowing silent integer overflow is the wrong choice for Rust, it's barely the wrong choice, because the costs and benefits are clearly arguable and user-specific.
Seems reasonable to me, and I certainly didn't demand overflow-checking by default in Rust. (OTOH, twelve hours ago you were so disappointed with that decision that it undermined your confidence in the Rust team...) But I don't see what that has to with picking the definitions for desirable adjectives like 'safety' when it comes to promoting or describing a system.
I also think overflow errors should be conveniently handleable without putting a try! or match expression on every operation, and so should other errors, but I'll spare you that rant :-)