Rust has its issues and there are plenty of things to not like about Rust, but this article is giving me the impression that this person has not written much Rust. Unfortunately, many such cases with Rust criticism.
> Memory safety is not that sacred. In fact, for many applications malfunctioning is better than crashing — particulary in the embedded world where Rust wants to be present. You cannot get 99.999% reliability with Rust — it crashes all the time.
Yeah until that memory safety issue causes memory corruption in a completely different area of code and suddenly you're wasting time debugging difficult-to-diagnose crashes once they do start to surface.
There were multiple failures before that `unwrap()` and the argument can easily be made that this is no different than an unchecked exception or a release assertion.
> Sync/Send, Mutex and reference counting (Arc)? Unfortuantely, those lock or simply mess with CPU caches badly, so they are inefficient for multithreaded communication, at least an intensive one. They are safe, but inefficient. Which kinda destroys the first uncompromising thing in Rust — performance.
Doing this the "correct" way in other languages has similar impact? So I'm not sure why Rust forcing you to do the correct thing which causes perf issues is uniquely a Rust issue. Doing this the "just get it done" way in other languages will likely come back to bite you eventually even if it does unblock you temporarily.
There are plenty of times I did a `static mut` global in Rust just to get some shit done and oops, accidentally hit some UB as the project grew.
>Yeah until that memory safety issue causes memory corruption in a completely different area of code and suddenly you're wasting time debugging difficult-to-diagnose crashes once they do start to surface.
Some very solid argument here. However, as already implied in my article, you can get most of the guarantees without losing your sanity. Memory-safety-related problems are important, but they are not the sole source of bugs in applications — as developers of Zed found out.
>Doing this the "correct" way in other languages has similar impact? So I'm not sure why Rust forcing you to do the correct thing which causes perf issues is uniquely a Rust issue. Doing this the "just get it done" way in other languages will likely come back to bite you eventually even if it does unblock you temporarily.
It might be counterintuitive, but garbage collectors in multithreaded code can be very efficient. I mean you just spawn lots of objects with random references in between and then eventually GC untangles the mess — it's zero overhead until the GC cycle. Escape analysis and semantic-reach containers can reduce GC work a lot (so you don't have the classical JVM GC problems). More specialized things like RCU and general quescence-state reclamation can be totally pause-less.
> Some very solid argument here. However, as already implied in my article, you can get most of the guarantees without losing your sanity.
I think this is part of why your article is causing a strong reaction from a lot of people; quite a lot of the justification for your point of view is left implied, and the concrete examples you do give about Rust (e.g. `Arc<Mutex<Box<T>>>>` and `.unwrap`) are hard not to see as straw men when plenty of people write Rust all the time without needing to rely on those; it turns out it's also possible to get more reliability out of Rust for those people without losing their sanity.
Most of the opinions you give are hard to distinguish from you personally not finding Rust to provide a great experience, which is totally valid, but not really indicative of "core problems" in the language. If you instead were making the argument of why you don't personally want to write any code in Rust, I don't think I'd have much criticism for your point of view (even though I wouldn't find much of it to apply to me). Then again, the article starts off with a statement of previously having made an intentionally provocative statement purely to try to compensate for a perceived bias in others, so maybe I'm just falling for the same bit by trying to engage the article as if it's serious.
Also the argument with `Arc<Mutex<Box<T>>>>` boils down to "Rust makes it hard to work with automatic reference-counted shared mutable heap-allocated state." In which case... mission accomplished? Rust just made explicit all the problems that you still have to deal with in any other language, except dealing correctly with all that complexity is such a pain that you will do anything you can to avoid it.
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.
Yeah, this pretty much summarizes my experience. In most situations, the actual information I need to share between different contexts is quite small, and using a channel ends up being pretty clean. There are still times I need an actual reference counted wrapper around a mutex, but they're the exception rather than the norm, and even then, there are often ways you can reduce the contention (e.g. if `T` is a type you can clone, and you only need a snapshot of the current state of the data rather than preventing changes while processing based on that snapshot, you can just clone it, drop the mutex guard, and then proceed without needing to worry about borrows).
It is hard to use because you can’t just access the shared state. You have to annoyingly lock it, handle the guard object, etc. Each one of those layers has protocols.
Of course you have to lock a mutex. You'd have to do that no matter the language, right?
And that's what I meant by verbose/ugly. Each of those steps is usually an if let / else error. None of those steps are hard, but you have to do them every time.
Heh. You're pedantically correct, the best kind. I meant "have to" in terms of "for correct operation you must" not "the compiler forces you to" since the latter isn't a thing in some languages.
> 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.
This is also my experience. I've had dozens of times over the years that I've been confused about why Rust does something a certain way, but I don't think I can recall a single one where I didn't end up finding out that there was an extremely good reason for it. That's not to say that in every one of those cases, the design choice Rust made was the only reasonable one, but in the cases where it wasn't, the situation always ended up being more nuanced than it appeared to me at first glance, and what I would have originally expected would have implicitly made a significant tradeoff I hadn't considered.
I'm having trouble finding it now, but years ago someone had a quote that ended up getting published in the language's weekly newsletter that was something like "Rust moves the pain of understanding your program from the future to the present". It's extremely apt; most of the difficulty I've seen people run into when trying to program in Rust ends up being that they're forced to consider potential issues that in other languages they could have ignored until later, and then would have had to debug down the line.
There’s a handful of pain points that the Rust model does impart which are not fundamental. For example, unsafe code is required to get a mutable borrow to two different fields of a struct at the same time.
But really that’s the only example I can think of offhandedly, and I expect there are not many in total. Nearly all of the pain is merely the trouble of thinking through these issue upfront instead of kicking the can down the road.
The only other one I can think of is kind of poor ergonomics around "self-borrows"; it's a lot less common than I've felt the need to mutably borrow two fields from the same struct independently, but there have very occasionally been times where I've realized the simplest way to structure something would be to have one field in a struct borrow another. This is somewhat hard to express given the need to initialize all of the values of a struct at once, and even if you could, there are some complications in how you could use such a struct (e.g. not being able to mutate field `a` safely if it's borrowed by field `b`). Overall though, the things that Rust prevents me from handling safely because of its own limitations rather than them being fundamentally unsafe are quite rare, and even with those, they tend to be things that I would be quite likely to mess up in practice if I tried to do them in a language without the same safety rails.
>the things that Rust prevents me from handling safely because of its own limitations rather than them being fundamentally unsafe are quite rare, and even with those, they tend to be things that I would be quite likely to mess up in practice if I tried to do them in a language without the same safety rails.
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.
>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.
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.
>However, as already implied in my article, you can get most of the guarantees without losing your sanity.
Yeah sure, but what compares that gives you similar perf, safety, and language features to Rust? I'll use "safety" in a loose term to say "you really infrequently encounter odd memory safety issues". Go for example still has the occasional memory corruption issues with maps in particular although these don't show up too often and the race detector exists.
C# is probably the closest I can think of for AOT? I don't really know what the deployment story for a .NET application looks like these days though.
Go has some language design things that turn people off.
>but they are not the sole source of bugs in applications — as developers of Zed found out.
You called out Zed in the blog post as well but I've not seen the Zed devs expressing regret of using Rust. Is this just an assumption on your part? As someone who's written many UI applications with egui and one with GPUI, I've felt some minor pain points but nothing show-stopping.
I used to write a lot of C#. I used to write a lot of Go. I now write either and basically exclusively write Rust these days and a bit of C/C++ at work. The only time I've really felt the pain of `Rc<RefCell<...>>` gross types was recently when trying to port a game engine's data loader to Rust. It makes heavy use of OOP patterns and trying to emulate that in Rust is asking for a bad time, but I did it out of just out of trying to one-shot the migration and keep logic similar. Not fun, but I knew what I was getting myself into.
A lot of things that people wanted in Golang (or in a C alternative), are in the newer language, Vlang[1][2]. Sum types, option/result types, enums, no variable shadowing, flexible memory management (GC can be turned off and stdlib does not rely on), generics (way before Golang's handlers relented to the masses), more safety features, goodies like filter, map, etc... As Vlang is 75% similar, it's also easy to learn and retains the high readability.
>Yeah sure, but what compares that gives you similar perf, safety, and language features to Rust?
I've already answered it in the original article — Rust is already here, and better language is not. Still, it will not make me say "it's the best option we have by now" — because it's not nearly the best option.
Performance? Lots of code is cold and not impacting performance much. You just don't need everything written in Rust or C++.
>You called out Zed in the blog post as well but I've not seen the Zed devs expressing regret of using Rust. Is this just an assumption on your part?
I'm kinda saying if I was a Zed dev I would have my pillow wet with tears at night. I know this because I participated in IDE development in C long time ago, and I was drowning in this whole low-level stuff all the time, I just could not do a single feature because I have to handle hundred of those other small things before the feature can work.
>As someone who's written many UI applications with egui and one with GPUI, I've felt some minor pain points but nothing show-stopping.
I have no idea what those applications were and how complex they were, so I cannot comment on it.
> It might be counterintuitive, but garbage collectors in multithreaded code can be very efficient.
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.
> It might be counterintuitive, but garbage collectors in multithreaded code can be very efficient.
It is indeed. But on the flip side, no other programming language is going to give you a compile error if you forgot to wrap you data into a mutex before sharing it between threads, and you'll either end up with a ConcurrentModificationException exception at runtime (Java) or with an undefined behavior.
But otherwise yes, there are plenty of situations where a GC is a totally valid solution. Just not for most of Rust's niche.
> Memory safety is not that sacred. In fact, for many applications malfunctioning is better than crashing — particulary in the embedded world where Rust wants to be present. You cannot get 99.999% reliability with Rust — it crashes all the time.
Yeah until that memory safety issue causes memory corruption in a completely different area of code and suddenly you're wasting time debugging difficult-to-diagnose crashes once they do start to surface.
> We actually had a recent Cloudflare outage caused by a crash on unwrap() function: https://blog.cloudflare.com/18-november-2025-outage/
There were multiple failures before that `unwrap()` and the argument can easily be made that this is no different than an unchecked exception or a release assertion.
> Sync/Send, Mutex and reference counting (Arc)? Unfortuantely, those lock or simply mess with CPU caches badly, so they are inefficient for multithreaded communication, at least an intensive one. They are safe, but inefficient. Which kinda destroys the first uncompromising thing in Rust — performance.
Doing this the "correct" way in other languages has similar impact? So I'm not sure why Rust forcing you to do the correct thing which causes perf issues is uniquely a Rust issue. Doing this the "just get it done" way in other languages will likely come back to bite you eventually even if it does unblock you temporarily.
There are plenty of times I did a `static mut` global in Rust just to get some shit done and oops, accidentally hit some UB as the project grew.