I don't think it's true of many real world use cases and if anything is only true of perhaps trivial use cases, where an application is run on a computer that has no other sensitive information and is not connected to the Internet.
I think what's true is that many programs don't ever reach a point where they're popular enough that someone can reliably exploit it. The threat of memory related errors has not much to do with use case and more to do with whether someone cares enough to identify that error, can reliably reproduce it, and whether the application that exhibits that error is on enough computers that exploiting it is profitable. If those three conditions are satisfied then it matters not whether the program is a video game, a word processor, an image viewer or whatever.
(And the funny thing about "trusted" communications: they're trusted until an attacker figures out how to store and forward untrusted data, at which point a logic error in the server itself is now nicely composable with code that "didn't need" to be particularly memory safe.)
If you're running a single threaded cloud lambda, you could allocate into an arena and never free, so you don't get UAF or DF.
This may not be a big deal for a toy game like Tetris. In a multiplayer AAA game it could lead to a bunch of bitter consequences though, from malware pwning customers' computers to ruined multiplayer experience because the bug would affect the server.
I'd say that a memory safety bug is not like a blemish on the surface of a building, unsightly but tolerable. It's more like a crack in the foundation, which may look tiny on the surface, but who knows how deep it goes.
My comment was about the tradeoffs we make for our memory safety guarantees, and whether they're always worth it.
I have some doubt that Rust feature velocity is meaningfully worse than C++ or C. I would bet good money the other way to be honest.
It also depends on one's familiarity with the languages, I've used both pretty extensively and can't comment on the velocity when just learning.
Just my experience, YMMV.
When you're just learning it Rust can definitely be a lot less productive and a lot more frustrating. But once you get to an expert level I think there's just no contest, and you can churn out code high quality code significantly faster than you can in C++. (At least that's how it is for me, and I've been programming C++ for over 10 years and I'm don't intend to ever go back.)
Indeed. IME (around 10y C++, mostly c++14 and c++17, and 6y Rust), I measure x2-x3 velocity in Rust, a lot of which comes from the tooling, a part of which from ub-freedom and much easier and safer parallelism, and the rest from the expressive type system and functional polish.
This is for initial development. Factoring in the maintenance over the years I suspect it tends to an order of magnitude of difference.
It’s impossible to verify, but some of the workarounds in very large c apps seem like they have about as much overhead as A modern GC.
The effectiveness of some key optimizations were sensitive to how tight we could make tail latencies for important operations, so a GC always had an unacceptable cost to the extent it occasionally introduced a stall at an inopportune moment. If GC stalls were more like 10 microseconds, consistently, that would be much more viable. I have no idea if that is realistically achievable.
Though for completeness’s sake I will add that in case of the JVM, variable tail latency still can be experienced due to the JIT compiler.
GC's don't exist in abstract, and there are plenty of GC enabled languages that also have value types and manual memory management capabilities.
There is no need to throw everything away when the right GC enabled language is chosen in first place.
On Google Earth, even sub-millisecond pauses would have been too much, we had to fight pretty hard for every sliver of performance we could get.
It would be nice if languages had Address Sanitizer built into the language. Maybe some sort of system of checking who owns and borrows memory at any point in time.
But contrary to expectations, I didn’t have many unsafe usages — they were all hidden under some abstraction (like allocate_object function of an ObjectPool), there is perhaps 7-8 in the whole (semi-partial) implementation? (Though it doesn’t yet have a GC, so there is that).
So all in all, in my anecdotal experience one can reasonably well hide the unsafe usages and then use the good expressivity of the language to combine these APIs in strictly safe ways. I also ran the program regularly through MIRI (a rust AST interpreter, which is similar to Valgrind in goals for my use-case) and whenever I found a memory bug I could pin-point it to a few lines of code at most, greatly speeding up debugging.
On Google Earth, garbage collection pauses weren't really acceptable, but we also wanted to add new functionality without being slowed down. Rust is great for a lot of reasons, but as many know, it can be slower on the feature velocity front.
In some AAA games, they need much more flexibility than the borrow checker thinks is appropriate. At some point, one has so many unsafe blocks that undermine the guarantees of surrounding code, that it makes more sense to use a different paradigm.
I know it's taboo to say this, but in many real world use cases, memory safety bugs aren't that bad and have the severity of any other logic error. Having a few more bugs per release might be worth it, if it means not dealing with the borrow checker's restrictions around abstraction, encapsulation, and polymorphism. You just fix them and push a new release.
Also, the problem is nicely mitigated by tools like Address Sanitizer, and will get even better with upcoming advances such as memory tagging and generational references, all which detect memory problems with surprising efficacy and accuracy.
In short, manual memory management is the right choice when Rust's drawbacks are a bit too much, and benefits aren't enough, for a particular situation.
Just my two cents!