The OP points out the wordyness of Go's error syntax. That's a good point. Rust started with the same problem, and added the "?" syntax, which just does a return with an error value on errors. Most Go error handling is exactly that, written out. Rust lacks a uniform error type. Rust has three main error systems (io::Error, thiserror, and anyhow), which is a pain when you have to pass them upward through a chain of calls.
(There are a number of things which tend to be left out of new languages and are a pain to retrofit, because there will be nearly identical but incompatible versions. Constant types. Boolean types. Error types. Multidimensional array types. Vector and matrix types of size 2, 3, and 4 with their usual operations. If those are not standardized early, programs will spend much time fussing with multiple representations of the same thing. Except for error handling, these issues do not affect web dev much, but they are a huge pain for numerical work, graphics, and modeling, where standard operations are applied to arrays of numbers.)
Go has two main advantages for web services. First, goroutines, as the OP points out. Second, libraries, which the OP doesn't mention much. Go has libraries for most of the things a web service might need, and they are the ones Google uses internally. So they've survived in very heavily used environments. Even the obscure cases are heavily used. This is not true of Rust's crates, which are less mature and often don't have formal QA support.
Ultimately, if you have to ask, the Rust vs. Go consideration boils down almost completely to "do you want a managed runtime or not". A generation of Rust programmers has convinced itself that "managed runtime" is bad, that not having one is an important feature. But that's obviously false: there are more programming domains where you want a managed runtime than ones where you don't.
That's not an argument for defaulting to Go in all those cases! There are plenty of subjective reasons to prefer Rust. I miss `match` when I write Go (I do not miss tokio and async Rust, though). They're both perfectly legitimate choices in virtually any case where you don't have to distort the problem space to fit them in (ie: trying to write a Go LKM would be a weird move).
The Rust vs. Go slapfight is a weird and cringe backwater of our field. Huge portions of the industry are happily building entire systems in Python or Node, and smirking at the weirdos arguing over which statically typed compiled language to use. Python vs. (Rust|Go) is a real question. Rust vs. Go isn't.
"This is the area where Go genuinely shines, and it’s worth being precise about why"
"the lack of GC pauses is a genuine selling point"
"Humans are genuinely bad at reasoning about memory"
"There are cases where the borrow checker is genuinely too strict"
tbc I don't think the article was fully AI-generated, just AI-assisted. If so, the author did a genuinely good job of it! No one else is commenting on it, so clearly it didn't detract much from the substance. It's just weird that this is becoming increasingly common, and increasingly hard to detect.I do have one nitpick though: Stating that data races are "caught at compile time" in Rust feels like it is overstating the case, at least a little. It sounds a bit like its implying Rust can also handle things like mutual lock starvation, or other concurrency issues. When that's simply not the case. I know "data race" is technically a formal term, with a decently narrow scope, yet I still think it could be a bit clearer about it.
After writing web services, GUI apps and terminal apps professionally in Rust, I honestly struggle to see a use case for other languages.
This is at the very least misleading, given that you can use unwrap.
Regarding error handling: will a parser error in the config return an error that includes the name of the file that’s failed to parse? That’s the kind of useful context that I add to errors in Go.
I will say that many of the issues with Go in the article, especially re: nil handling are increasingly solved by thorough coding reviews with Codex. Better to not have the issue in the first place, sure, but these kinds of security bugs are becoming optional to developers who put in at least as much effort to review and understand code as they put into the initial design and execution.
Language data at https://gertlabs.com/rankings?mode=agentic_coding
> "Go got generics in 1.18 (March 2022), thirteen years after the language shipped. They are useful, but they feel tacked on, and in practice they have most of the downsides of a generic type system without delivering the upsides you’d expect coming from Rust, Haskell, or even modern C++."
The problems with Go generics have now largely been solved, haven't they? Is this comment from the author still applicable?
I have a huge list of things that I have in Rust that I would like in Go, but I don't have a single thing I am missing from Go in Rust.
I grow tired of golang "dumb it down" approach as I find it actually just shifts more and more work onto me.
Is anyone in a different position? What does Go have that rust does not?
Kind of funny when your Rust service runs on Kubernetes.
I don't know why anyone uses spawn_blocking for CPU-bound tasks. It's clearly designed for blocking IO tasks. There's a reason why Erlang cordons them separately into Dirty CPU and Dirty IO schedulers.
In the article, if you were to mention & follow them GetUser() in Go becomes user() in Rust[1], not get_user().
[1] https://rust-lang.github.io/api-guidelines/naming.html#gette...
Over the past year I've been using AI to write small Rust tools for myself — I barely read the code, and honestly it just works.
But for serious projects I expect to maintain long-term, I still pick Go. Today I want code I can actually own and reason about myself.
Give it a year or two and I probably won't be writing code by hand at all. Once the AI owns the code anyway, that reason disappears — and at that point Rust's guarantees win. So I suspect I'll end up leaning Rust.
cargo audit is not built-in, it is 3rd party. (The comparison table near the top isn't clear about that, and the following text stating more is built-in for Rust than for Go might be confusing. I would suggest adding an asterisk to mark built-ins in that table.)
cargo watch has been in "maintenance mode" for some time. The author of that suggests cargo bacon instead.
It feels like yesterday when every single project was moving to Go just because it was the new hype, that was until Rust was born.
We are already seeing projects dumping migration to Rust because the grass is not always greener on the other side.
We will be seeing this again, "Migrating from Rust to XYZ"
[1] https://github.com/polynya-dev/pg2iceberg
[2] https://www.polarsignals.com/blog/posts/2024/05/28/mostly-ds...
A lot of libs/packages in Go's stdlib also has this problem. They like to package everything in a very tight interface (very obvious example includes crypto/* and http), without exposing implementation detail to the end user.
Doing this of course has it's benefits, but if the feature provided by the stdlib slightly don't fit you needs, then you might have to write your own (potentially unsafe and/or less performant) one from zero.
Rust is great overall, but there's some oddities. For example their lib.rs / `mod` is very, very unintuitive, it felt overdesigned and unnecessarily complex (just see [their book]). I like what Go or Java did to their lib/package systems, it's much better that way.
[their book]: https://doc.rust-lang.org/stable/book/ch07-05-separating-mod...
The biggest gripe I have with Go is the lack of *any* compile time check for mutex. Even C++ has extensions like ABSL_GUARDED_BY. For a language so proud on concurrency, it is strange not to have any guardrails.
There was a signal to assist c++ to plain and simple C AI mass migration.
Removing any languages with ultra-complex syntax towards simple and plain C is always a good thing.
Now Rust is the new Go.
I find that very confusing.
But Go to Rust???
It does not make any sense.
Lmao so not an equivalent then? Standard glibc malloc, which is default in rust, will also similarly degrade albeit for different reasons.
If those are issues, I rather use C#/.NET than expose both developers and AI agents to a cognitive overload.
However, those are not big issues to me, and at least in the present day, Go seems to excel at the things it is supposed to: backend and microservices. Sure, you can find some small issues with Go if you are really nitpicking, but you can find bigger issues with other languages. Sure, Go is boring as f..k, but I don't care and the agents don't mind, they love Go. Most people prefer reading Go than reading Rust. Go allows a fast way to production and for many startups and small companies, that matters a lot.
I don't hate Rust, and even use it - for where I think it makes sense, but for backend and microservices, Go seems a better fit.
As always, this is an opinion, derived from my personal experience, take it with a grain of salt, your experience might be different.
It also skips entirely over debugging (delve vs gdb), IDE support, ecosystem (why the hell does Rust have N async runtimes?!), statically linking and so on.
A comparison between the performance of RLS / rust-analyzer (painfully slow) and gopls would be enough to kill the whole argument about developer happiness and productivity.
It even passes traits as a "reason to switch" to Rust - where in fact it would probably be a reason (IMHO) not to use it (together with lifetimes).
I think both languages are amazing, so a migration Go -> Rust (or Rust -> Go) makes no sense most of the time.
I've written code in both for a while now, so I know the pain and advantages of both.
For example, Go sucks at microcontroller stuff - in fact it's not even Go officially (see my presentation about porting "Go" to an ESP32-S3 [1]) - whereas Rust is amazing and even has a strong project behind (https://esp.rs) and amazing tooling (probe-rs & co).
What's also not addressed here is the Go ecosystem. The Go packages are one `go mod add` away (pkgs.go.dev) and the module owner guarantees v1 backwards compatibility for the whole lifetime of the module. This means that, no matter what happens, your dependencies will always be up-to-date with no migration struggle. This makes creating stuff for anything around the Kubernets ecosystem a breeze, you can literally import the types from another project and start your integration right away.
The most valuable part of the article seems the link to the opposite view (https://blainsmith.com/articles/just-fucking-use-go/). They're equally biased, but one is more straightforward than the other.
All in all, it's not a fair comparison and it's very biased (which is fair) - at the same time I think the idea behind the article is "wrong". If you find yourself migrating from Go to Rust (or vice versa), you're likely doing something wrong - and the performance gain is not the reason you're really doing it for.
[1]: https://docs.google.com/presentation/d/18jWccV-F2FguZiB5gXLk...
>The other prior worth disclosing: I run a Rust consultancy; of course I’m biased!
This can easily be justified for many usecases, but for your vanilla crud app, do you really need Rust?
Per the article, you are getting 20-50% better more performance with Rust. Not worth it unless your team was already fluent in Rust. Now consider a scenario where your team uses AI exclusively to code, now you are spending more time and tokens waiting around to consume large rust builds. As far as I know this is an inherent property of Rust to have its safety guarantees.
I think Rust makes sense for a lot of cases, but for a small web service, overkill and unnecessary imho. If someone ported their crud app from Go to Rust I would question their priorities.
Again I am speaking more in terms of software engineering economics than anything else. Yes, I know in a perfect world Rust binaries are smaller, performance is better and code more “correct”, but the world is hardly perfect. People have to push code quickly, iterate quickly. Teams have churn, Rust, frankly is alien for many, etc.