Any language with GC can handle complex links between object, including mutable object. Like Erlang/Elixir, JS, Go, etc. You message implies runtime-less language, but majority of practically employed languages are runtime-full.
I don't, but that's not to say that I think it should never be done.
> Formally you are correct, but there are many things in C++ that should have better not existed.
Sure, but I think it's important that one should do their best to be correct and/or precise.
> Because it would lose most of the Rust properties by that time.
Perhaps for specific bits of code, but that doesn't necessarily require that your entire codebase give up on safety. Part of Rust's value is in isolating unsafe stuff to specific sections of your codebase both so the rest of the code can make use of Rust's guarantees and so if/when something goes wrong it's easier to pinpoint the problem.
Not to mention if you're talking about "specially designed" codebases in the kind of situation you describe you're almost certainly not in pure-C-land either (e.g., standard C doesn't have the concept of CPU registers, so if you really need to stick to what's in registers you're going to have to resort to compiler-specific extensions and/or assembly). If you're willing to allow the necessary extensions for C, it's only fair that you do the same for Rust.
> I'm not saying that you are wrong though, there might be people optimizing Rust for this very purpose, but I'm not aware of such an effort.
There's a reason no_std exists. Low-resource embedded use has been a design focus since well before Rust's 1.0, and those considerations have continued to influence its evolution since - for example, Rust's async design is the way it is specifically to ensure it is usable in limited-resource environments.
> Who's gonna GC the poisoned garbage left in undefined state after the crash?
Whatever supervising process/thread you write/designate, if that kind of recovery is important to you? I don't think there's anything about Rust that precludes you writing such a thing.
Not to mention, must there be "poisoned garbage" in the first place? I don't think it's strictly necessary that such a thing be produced after a crash even if you ignore the fact that part of the reason unwinding exists is to clean things up even while crashing.
> but from what I know it's rather in middle of "not possible" and "not viable".
I'm curious how you came to that conclusion. It seems wrong both on a theoretical level (Drop/unwinding/catch_unwind should obviously suffice for at least some cases?) and on a practical level (tokio can recover from worker thread panics just fine?).
Tokio provides crash-resistant synchronization primitives, but it cannot recover complex structures you've been handling — you either need to write panic-handlers that would revert your data to more-or-less manageable state or employ ready-made libs that do it for you. Languages designed for crashes don't require ad-hoc recovering — they just automatically clean up everything. That's what I've called "viable".
I mean you could have written crash recovery in C++, but it's just not viable because of how many things can go wrong.
Again, mission f#*@ing accomplished. Maybe you DON'T need that state to be shared, reference-counted, or heap allocated. Maybe you can refactor your code to get rid of those annoyingly hard to deal with abstractions. And you end up with better, more reliable, likely faster code at the end of it.
So many times I've tried to do something in Rust the old fashioned way, the way I have always done things, and been stopped by the compiler. I then investigate why the compiler/language is being so anal about this trivial thing I want to do.. and yup, there's a concurrency bug I never would have thought of! I guess all that old code I wrote has bugs that I didn't know about at the time.
There are basically two reactions people have to this situation: (1) they're thankful that they learned something, their code is improved, and go about their day learning something new; or (2) feel frustrated and helpless that the old way of doing things doesn't work, and rage-quit to go write a "WHY RUST IS THE WORST THING SINCE MOSQUITOS" blog article.
That's the point 4 in my article — Rust is horrible for mutable shared state. However, in the modern CPU-based programming mutable shared state is like 70% of all the code, so you cannot just ignore it. It's not that CPU-s have to be like that, it's they hapened to be like that.
>there's a concurrency bug I never would have thought of! I guess all that old code I wrote has bugs that I didn't know about at the time.
Programming languages or libraries that excel at concurrency do not use the Arc<Mutex<T>> nuisance. At least they are not imposing it as a main tool. Having shared mutable state does not mean you directly change cell there, like you would in C/C++. I mean if you have a cyclic graph of connected objects — how the hell are you gonna employ Arc<Mutex<T>> for handling them? What Rust actually does is carving in stone pathologic C/C++ ways of "correct handling of shared mutable state" — whatever it is.
>Please read the whole article. If the unwrap hadn't caused an exit, the process would've run out of memory, leading to a much less deterministic behavior which is much harder to diagnose and fix. I always prefer an early exit with a clear error instead of getting killed by the OOM reaper.
I am running into an undiagnosable CUDA "illegal memory access" problem in vLLM, a code base that is a mix of python and CUDA (via pytorch). At a certain load something appears to either overflow or corrupt the memory and vLLM restarts, which takes a minute, because it has to reload several dozens of GBs into memory and then rerun the CUDA graph optimizations.
The pacemaker argument is complete nonsense, because the pacemaker must keep working even if it crashes. You can forcibly induce crashes into the pacemaker during testing and engineer it to restart fast enough that it hits its timing deadline anyway. Meanwhile a silent memory corruption could cause the pacemaker to enter an unknown state where the code that runs the pacemaker algorithm is overwritten and it simply stops working altogether. Having a known failure state is a thousand times more preferrable to an unknown number of unknown failure states. Critical sections (mutexes) and unsafe code has to be panic free (or at least panic safe) in Rust, so the concept of writing code without panics isn't exactly a niche concept in Rust. For every panic based feature, there is usually a panic free equivalent.
I'm actually the one who promotes paranoidal assert-s everywhere. I do agree the original statement from the article is ambiguous, probably should have written something like "memory safety in Rust does not increase reliability".
>The pacemaker argument is complete nonsense, because the pacemaker must keep working even if it crashes. You can forcibly induce crashes into the pacemaker during testing and engineer it to restart fast enough that it hits its timing deadline anyway.
I'm not sure whether there is a deadlock-free modification of Rust — deadlock is not considered an undefined behavior in Rust.
Not a fact. Particularly in the embedded world, crashing is preferable to malfunctioning, as many embedded devices control things that might hurt people, directly or indirectly.
> If a pacemaker stops — telling a victim “but the memory was not corrupted in the crash” is a weak consolation.
If a pacemaker suddenly starts firing at 200Hz, telling a victim "but at least it didn't crash" is a weak consolation. A stopping pacemaker is almost always preferable to a malfunctioning one, as most people with pacemakers still have sufficient natural rhythm to survive this for long enough to get help.
> We actually had a recent Cloudflare outage caused by a crash on unwrap() function
Please read the whole article. If the unwrap hadn't caused an exit, the process would've run out of memory, leading to a much less deterministic behavior which is much harder to diagnose and fix. I always prefer an early exit with a clear error instead of getting killed by the OOM reaper.
It really depends on how deeply Turing you mechanism is. By being "Turing" I mean "the behavior is totally dependant on every single bit of previous information". For a reliable system turing-completeness is unacceptable for separate functions i.e. it should produce a correct result in a finite amount of time no matter what hapened in the past. Particulary, that's why modern real-time systems cannot be fit into Turing machine, because Turing machine has no interrupts.
>If a pacemaker suddenly starts firing at 200Hz, telling a victim "but at least it didn't crash" is a weak consolation. A stopping pacemaker is almost always preferable to a malfunctioning one
You almost make an excuse for general unreliability of programs. Mainstream C is unreliable, C++ is unreliable, Rust is unreliable. I can agree that Rust is not less reliable than C/C++, but it is definitely less reliable than some other language e.g. BEAM-based ones. I mean in Rust standard library some time ago I actually read "in these and these conditions the following code will deadlock. But deadlock is not an undefined behavior, so it's ok". The designers of Rust did not really try to support any kind of "recover and continue" way of functioning. Yes, you can catch the panic, but it will irreversibly poison some data.
> Its compilation is slow. I mean SLOW. Slower than C++.
No way. Maybe Rust 1.0, but it's steadily improved and it's definitely faster than C++ now.
> It’s complex. Just as complex as C++.
True, but the problem with C++'s complexity is that you have to memorise all of it or you'll accidentally invoke UB. It's so complex that is basically impossible.
Rust is complex but most of the time the compiler will tell you if you got it wrong. There are exceptions of course (lots of async footguns) but it's still night and day.
> Memory safety is not that sacred. In fact, for many applications malfunctioning is better than crashing
Not sure I really need to counter this...
> When handling lots of mutable shared state (GUI, DB, stateful services, OS/hardware), the performance of native Rust memory model is subpar, and non-native unsafes just leave you with slow compilation, high complexity, and no memory safety in the end — which makes Rust practically meaningless for heavy mutable state jobs.
Not totally clear what he's getting at here. Maybe the ergonomics of GUI style programming which are still being figured out? Hardly a deal breaker though is it? There are plenty of C/C++ GUI libraries with terrible ergonomics and the only one that is actually great (Qt) had to use a custom language extension for decades to achieve that.
> So, is the Rust bad or good? It’s neither. It’s a mediocre programming language with thousands of man-month put into its development
I would love to hear what he thinks a good programming language is, because I can easily pick more holes in any other language than he has.
This anti-Rust zealotry is super tedious.
I have a working theory that the classic programming is basically dying out: https://bykozy.me/blog/llm-s-are-not-smart-rather-programmin... You just cannot write fast and reliable program neither with C++ nor with Rust, because the fundamental model is broken and nobody's really bothered fixing it.
What has garbage collector to do with multithreaded code? Once you have two or more threads which needs to share data, they need to sync and you'd end up using some kind of lock, which will affect the performance. GC doesn't make anything efficient or less efficient here. It might make the code simpler as you don't have to worry about allocation/deallocation, but I don't see how it's magically going to remove the lock.