| There’s a lot to criticize about Rust for sure, but I feel like some of the points here aren’t necessarily in good faith. > Explicit destruction (under the hood) of every object. It's slow. Care to actually support this with data? C++ is quite similar in this respect (Rust has a cleaner implementation of destruction) and generally outperforms any GC language because stack deallocation >> RC >> GC in terms of speed. There’s also a lot of good properties of deterministic destruction vs non deterministic but generally rust’s approach offers best overall latency and throughput in real world code. And of course trivial objects don’t get any destruction due to compiler optimizations (trivially live on the stack). And zig isn’t immune from this afaik - it’s a trade off you have to pick and zig should be closer since it’s also targeting systems programmers. > - Many memory-safe programs won't type-check (impossible to avoid in any perfectly memory-safe language, but particularly annoying in Rust because even simple and common data structures get caught in the crossfire). Actually most memory safe languages don’t have issues expressing data structures (eg Java). And rust has consistently improved its type checker to make more things ergonomic. And finally if you define rust as language + stdlib which is the most common experience those typical data structures are just there for you to use. So more of a theoretical problem than a real one for data structures specifically. > Even in safe Rust, you still have a form of subtle data race possible, especially on ARM. I agree that for the language it’s weird that this is considered “safe”. Of course it’s not any less safe than any other language that exposes atomics so it’s weird to imply this as something uniquely negative to Rust. > Even in safe Rust, races like deadlocks and livelocks are possible. I’m not aware of any language that can defend against this as it’s classically an undecidable problem if I recall correctly. You can layer in your own deadlock and livelock detectors that are however relevant to you but this is not uniquely positive or negative to rust so again weird to raise as a criticism of Rust. > The constraints Rust places on your code tend to push people toward making leaky data structures. In every long-running Rust process I've seen of any complexity (small, biased sample -- take with a grain of salt), there were memory leaks which weren't trivial to root out. I think you’re right to caution to take this with salt. That hasn’t been my experience but of course we might be looking at different classes of code so it might be more idiomatic somewhere. > In the remaining use-cases, the right thing to do is almost always to put everything into a container object with its own lifetime You can of course do that with Rust boxing everything and/or putting it into a container which reduces 99% of all lifetime complexity. There are performance costs of doing that of course so that may be why it’s no considered particularly idiomatic. My overall point is that it feels like you’ve excessively dramatized the costs associated with writing in Rust to justify the argument that memory safety comes with excessive cost. The strongest argument is that certain “natural” ways to write things run into the borrow checker as implemented today (the next gen I believe is coming next year which will accept even more valid code you would encounter in practice although certain data structures of course remain requiring unsafe like doubly linked lists which should be used rarely if ever) |
Comparing stack deallocation vs GC is kinda weird because it's not an either-or - many GC languages will happily let you stack-allocate just the same (e.g. `struct` in C#) for the same performance profile. It's when you can't stack-allocate that the difference between deterministic memory management vs tracing GC become important.
Also, refcounting is not superior to GC in terms of speed, generally speaking, because GC (esp. compacting ones) can release multiple objects at once in the same manner as cleaning up the stack, with a single pointer op. Refcounting in a multithreaded environment additionally requires atomics, which aren't free, either. What refcounting gives you is predictability of deallocations, not raw speed. Which, to be fair, is often more important for perception of speed, as in e.g. UI where a sudden GC in the middle of a redraw would produce visible stutter.