Is a bounds check on every container access good for me? Not always. Sometimes my code is correct. But security vulnerabilities are observably a huge problem. And you only need one vuln in iMessage or whatever to get a dissident journalist's phone hacked and then get them murdered by the Saudi government. Is "I know what I'm doing, I promise" good enough for you in that case?
I find it interesting because until about three years ago, C++ was always on the other side of this argument. We fight about static type systems. You could shout at the compiler that you know that your code is correct even though the type system doesn't pass. Why does the committee think it knows better than me! Everything is just bits after all!
Now we are on the other side. C++ people saying "bro I totally promise I'll initialize this data member before it is used, stop making me initialize it in my constructor"
> Now we are on the other side. C++ people saying "bro I totally promise I'll initialize this data member before it is used, stop making me initialize it in my constructor"
C++ has never said this - if a type has a constructor, that constructor will always be used in a data member (or elsewhere).
zabzonk said "if a type has a constructor", but I don't think the int type does, which is why you get uninitialized memory here.
Any instance of Foo will run Foo's constructor because Foo has one. The problem is that that instance of Foo won't definitely have x initialized, because int doesn't.
Sure. If for some reason you only care about this with regards to struct types that have constructors that correctly initialize all of their members then you are good by default (sort of, there are weird edge cases here with globals). But "safe for this, not for that" is not especially compelling to me when it is hard to imagine a C++ codebase that doesn't use primitives/pod types somewhere.
Plus, there are stl types that don't guarantee initialization of members. So it isn't like only ever resting on top of stl types is sufficient. std::optional has a non-default constructor but it can happily blow up because of a read of uninitialized data.
And the fact that this is the silent default behavior in C++ is wild to me. Even in languages where it is possible to leave data in an uninitialized state, you should need to loudly signal it.
i said "if a type has a constructor". but if you are fiddling around with ints and chars and the like, then you will need to initialise them. i have been writing c++ since the mid80s and have never found this to be a problem, or at least no more than it would be to use an uninitialised int variable in C.
The fact this is even a distinction anyone has to keep in mind is again an example of the problem. People are awful at remembering these weird little details. We shouldn't expect people do things they're bad at, all the time, and to get them right. And I'll go a step further and say we shouldn't let them by default. They should have to make it super explicit they want to opt into the weird behavior nobody expects.
You can do this in Rust if you want! You just have to use unsafe { mem::uninitialized() } or even better, the newer unsafe { mem::MaybeUninit::uninit() }
The whole point is that you probably don't ever want to have an uninitialized variable you can access willy-nilly, and if you do, you should be very explicit.
Even your statement about C (and C++) isn't quite accurate, right, because anything static is guaranteed to be initialized to zero on creation. So while `int x` is a free for all, `static int x` is gonna be zero. Another thing people shouldn't have to think about!
"In some cases you are safe in other cases you aren't" is not good enough for real safety.
Congrats if you never write bugs. For the rest of us, we will use the widely available data that demonstrates that these bugs recur over and over and over and over even in organizations with strong developers, strong testing culture, and where the use of static analyzers and fuzzing is widespread.
Yes, C also has these problems. Both C and C++ represent significant safety risks, despite powering some of our absolute most security critical applications (the linux kernel and our web browsers). Other languages don't give you these footguns.
I find it interesting because until about three years ago, C++ was always on the other side of this argument. We fight about static type systems. You could shout at the compiler that you know that your code is correct even though the type system doesn't pass. Why does the committee think it knows better than me! Everything is just bits after all!
Now we are on the other side. C++ people saying "bro I totally promise I'll initialize this data member before it is used, stop making me initialize it in my constructor"