I think we've got the balance wrong, partly because some engineering considerations derive directly from separate compilation. ODR no diagnostic required doesn't have to be a thing any more.
Someone else posted statistics that show Firefox being 10% Rust, but I'm not sure it makes sense to include HTML and Python and JavaScript in the comparison. If you compare Rust against C/C++, it's 20%
There is no such language as C/C++. I presume you mean the sum of C and C++, and that you omit external libraries from the tally.
"I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX."
Pedant
>and that you omit external libraries from the tally.
Mozilla vendors their dependencies. They're counted.
When any Costco sells a desktop ten thousand times faster than the one I started on, we can afford runtime sanity checks. We don’t have to keep living like this, with stacks that randomly explode.
You can die of multiple illnesses so there's no point in develop treatment for a particular one of them.
> I like core dumps because the failure is immediate, obvious, and fatal.
Core dumps provide a terrible debugging experience, as the failure root cause is often disjoint from the dump itself. Not to mention that core dumps are but one outcome of memory errors, with other funnier outcomes such as data corruption and exploitable vulnerabilities as likely.
Lastly, memory safe languages throw an exception or panic on out of bound access, which can be made as immediate and fatal as core dumps. And much more obvious to debug, since you can trust that the cause starts indeed at the point of failure
Well, in modern C++ this code would look like this:
std::array<int, 4> table;
bool exists_in_table(int v)
{
for (auto &elem : table) {
if (elem == v) return true;
}
return false;
}
Or even simpler: std::array<int, 4> table;
bool exists_in_table(int v)
{
return std::ranges::contains(table, v);
}
There's no shortage of footguns in C++, but nonetheless, modern C++ is safer than C.It wouldn't suprise me if I have run into the latter situation without relizing it. When I got the the problem, I would have just (incorrectly) assumed that the memory right after the array happened to have the relevent value. I would be counting my blessings that it happened consistantly enough to be debuggable.
Reminds me of when for a while, I worked on HP 9000s under HP-UX and in parallel on an Intel 80486-based Linux box, and what I noticed is that the Unix workstations crashed sooner and more predictably with segmentation faults than Linux on the PC (not sure if this has changed since the early 1990s - probably had to do with the MMU); so developing on HP under Unix and then finally compiling under Linux led to better code quality.
That isn't idiomatic C++ and hasn't been for a long time. Sure, it's possible to do it retro C-style, because backward compatibility, but you generally don't see that in a modern code base.
> the compiler should optimize away bound checks most of the time (and in all critical places)
Unfortunately, this is true much rarer than you might think. In order to optimize it away, a compiler has to prove that the bounds check result is always true and that happens surprisingly not all the time to say the least. When it can't optimize it away, bounds checking will slow down the code significantly, especially in tight loops. And that slowdown will be very hard to debug unless you know exactly where to look for, - you'll basically assume that "that's how it works at the highest speed and it can't be improved (a.k.a. "buy a better hardware!")
Second, with a proper programming hygiene, in many cases bounds checking are just redundant. There are at least 2 methods for direct iteration over a vector, that doesn't require it: ranged `for (auto& e: vector){}` and by utilizing iterators. There are also `<algorithms>` library with implementation of many useful container iteration functions that require you at most to only specify a functor that do some operation on vector element.
And third - if you think that you really must have bounds checking, it's about as trivial to implement as:
template<typename T, typename A>
class vectorbc : public ::std::vector<T,A>{
public:
using std::vector::vector;
T& operator[](std::size_t idx){
return this->at(idx);
}
}Also use the compiler and language feature that help, such as variably-modified types such as bare pointers, attributes, compiler flags, etc.
My compiler (MSVC) doesn't do that[0]. Clang also doesn't do this[1]. It's wild to me that GCC does this optimization[2]. It's very subtle, but Raymond Chen and OP both say a compiler can create this optimization, not that it will.
[0]: https://godbolt.org/z/bdx4EMzxe