Rust vs. Go in 2026 — a calmer take
After five years of shipping both in production, my take has stopped being 'it depends' and started being 'it really depends — here is precisely on what'.
The Rust-vs-Go discourse online is, charitably, not great.
If you spend any time on Hacker News or the relevant subreddits you will get the impression that this is a fundamentally tribal question. One camp believes the borrow checker is the second coming and anyone still writing garbage-collected code is committing professional malpractice. The other camp believes Rust is a self-indulgent academic exercise and real engineers ship Go. Both camps tend to have very strong opinions on what kind of person prefers their opponent’s language.
I have, for the last five years, shipped meaningful production code in both. For most of that time my answer to “which should I use?” was a cowardly “it depends,” which is the answer that gets the most nods at meetups and the fewest fights, and is, on careful inspection, correct but useless.
In 2026 I think we have enough collective scar tissue to be a bit more specific. So here’s my attempt at a calmer version. Not the hot take. The cooler, slower take, where I try to commit to actual positions and explain them.
The TL;DR table
If you only have thirty seconds:
| Workload | What I’d reach for first | Why |
|---|---|---|
| HTTP/gRPC service, normal CRUD | Go | net/http, generics, and a 4-second compile beat everything else in this niche |
| Latency-critical, p99.9 budget under 1ms | Rust | No GC tail. The first time you see a 30ms Go GC pause in a hot path you’ll understand |
| CLI tool you want people to install | Rust, narrowly | Cargo + static binaries is the most painless distribution story going |
| Embedded, firmware, WASM | Rust | Go’s runtime is too heavy. This isn’t a close call. |
| Internal batch jobs and data pipelines | Go | Channels are the right shape for the work, throughput rarely the bottleneck |
| Library you want consumed from C, Python, Ruby, Node | Rust | C ABI + cbindgen is the lingua franca. Go’s FFI story is a journey nobody enjoys |
| Anything where the spec changes weekly for the next year | Go | The “blank page to production” gradient is gentler |
| Anything that has to be correct for the next five years | Rust | The type system compounds |
If your case isn’t in there, read on. If it is, you can probably stop reading and go to lunch.
What’s changed since 2021
It’s worth pausing on this because a lot of online discourse is still arguing about the 2020 versions of both languages.
Rust, in 2026
- Compile times are not the deal-breaker they were. They are
still slow. They are not “go make coffee” slow anymore. With
cargo check, workspace splitting,sccache, andmoldas your linker, an incremental rebuild on a medium-sized service is comfortably under ten seconds. It is still slower than Go’s equivalent, and probably always will be. You will live. - Async is, mostly, fine. Tokio is the default and won.
async fnin traits works withoutasync-traitceremony. TheSendbound and pinning footguns are still there but they’re documented now and most of the standard libraries handle them for you. - The ecosystem is enormous. The hard problem stopped being “is there a crate for this?” and became “which of the eleven crates is actively maintained?” This is, on balance, a better problem to have.
Go, in 2026
- Generics happened, and the sky didn’t fall. The stdlib has
absorbed them tastefully (
slices,maps,cmp). Go code in 2026 is materially more expressive than it was in 2021 without being less readable, which is a trick I would not have predicted. - The runtime got quietly, dramatically faster. GC pauses, goroutine scheduling, escape analysis: all materially better. The same Go service in 2026 vs 2021 with zero source changes is meaningfully faster.
- Modules stopped being a meme.
go.mod,go.sum,go workfinally feel like a coherent story. - The language has stayed deliberately small. This is the thing the Go team gets the least credit for, and it is, in my view, the most important property the language has.
Both languages, in other words, are noticeably better than the languages people are still arguing about online. Both are also more themselves — Rust has gotten Rustier, Go has gotten Go-ier. Which is to say: the trade-off between them is sharper now, not fuzzier.
Where Rust earns its keep
Three cases. I will name them.
Case 1: tail latency that actually has to hold. If your SLO is “p99.9 under a millisecond” and you can’t afford to burn entire cores on amortising GC, Go will, sooner or later, surprise you. Rust won’t. I’ve been part of two metrics-ingestion rewrites where the math came out in Rust’s favour by a factor of roughly 2.5x on throughput and an order of magnitude on tail latency. Those numbers are real and they were not the result of micro-optimising Go for six months first — we tried that, it helped some, the GC was still the GC.
Case 2: memory pressure you can’t predict. Streaming pipelines,
parsers, anything that fans in a million small allocations per
second. Rust’s lack of GC isn’t free — you pay for it in ownership
annotations and 'a lifetimes that occasionally make grown engineers
cry — but the worst case is predictable, which is precisely what you
need when there is no worst case in your test suite that survives
contact with reality.
Case 3: software you can’t easily redeploy. Firmware, agents in customer data centers, browser-side WASM, kernel modules, anything where “we’ll just push a hotfix” is not a sentence you get to say. Rust’s “if it compiles, it tends to work” property is, in this class of problem, the difference between a quiet on-call rotation and a recurring nightmare. I have lived both. I know which one I prefer.
A worked example, badly anonymised: a metrics ingestion service my team ran absorbed ~3.2M data points per second per pod, peak. It started in Go. It worked, but cost us 80 pods and uncomfortable p99.9 latency. We rewrote it in Rust over about ten weeks. Throughput went up by a factor of 2.4. p99.9 ingest latency dropped from ~4ms to ~700µs. Pod count went from 80 to 32. The rewrite paid for itself, conservatively, in seven weeks of saved cloud spend.
That kind of math is uncommon. When it shows up, it is very loud.
Where Go earns its keep
Three cases here too. Different cases.
Case 1: CRUD that’s shaped like CRUD. A frankly enormous fraction of internal services do this: take a request, call two other services, write to a database, return JSON. Go does this without ceremony. The stdlib does the boring parts. The code that comes out is approximately the code anyone on the team would write, in approximately the same way. That last property — everyone writes Go the same way — is genuinely valuable at scale and undervalued in language debates.
Case 2: teams that change a lot. A new hire is productive in Go in days. A new hire is productive in Rust in… longer. If your org has rotations, contractors, “we’re seconding one engineer for a quarter” arrangements, or anything else that churns the people on the codebase, Go’s onboarding gradient is straightforwardly friendlier. Rust pays back over years. If you don’t have years, you don’t get the payback.
Case 3: pipelines and orchestration. Goroutines plus channels remain the cleanest concurrency primitives in any mainstream language I’ve used. Tokio is great. It is not as natural for “a thousand small workers reading from a queue” as Go’s primitives are, and pretending otherwise is something I think the Rust community could be slightly more honest about.
The thing nobody mentions in these debates: the median Go program written by the median Go programmer is strikingly better than the median Rust program written by the median Rust programmer. Not because Rust is worse — it’s better, on raw expressiveness — but because Rust gives you more rope, and Go won’t let you go looking for rope at all. There’s a real virtue in a language that limits how clever you can be.
How I actually decide, in practice
When the technical answer is “either of these works”, which is honestly most of the time, here’s how I break the tie:
- Who is going to maintain this in three years? If the answer is “we have no idea, probably people we haven’t hired yet, possibly a contractor in a region with a thin Rust community” — lean Go. If the answer is “the same five people, with maybe one rotation” — lean Rust.
- What’s the cost of a runtime panic? Low — either works. High, in the way that costs money or trust or shows up in the news — Rust’s type system pays for itself.
- How much is the spec going to change in the next year? A lot — lean Go. The cost of rewriting Rust code under a moving spec is, in my experience, materially higher than the cost of rewriting Go code. The Rust pays off when the spec calms down. Until it does, you pay the tax for nothing.
Things I will not argue about
Some statements I see repeated online and have stopped engaging with, because they are either wrong, outdated, or bad-faith:
- “Rust is too complex to ship in a team.” Demonstrably false. Many teams ship Rust every week, including some of the largest infrastructure organisations on the planet. Pick a different argument.
- “Go isn’t a real systems language.” It runs Kubernetes. It runs Docker. It runs most of the cloud-native ecosystem your Rust service is deployed inside. It is, by any sane definition, a systems language.
- “GC is always a problem at scale.” It isn’t. Plenty of high-throughput services run garbage-collected runtimes. The question is whether your workload is GC-pressure-bound. Most workloads aren’t.
- “The borrow checker is just a fancy linter.” It is, and that linter prevents an entire category of CVEs. The CVE-prevention business is, depending on your industry, worth quite a lot of money.
What I’d actually do, on a Tuesday
If I were starting a new project tomorrow:
- A new product where everything’s in motion: Go. No hesitation.
- A service in our hot path with a meaningful latency budget: Rust.
- A CLI tool I plan to distribute to other engineers: Rust. Cargo ships single static binaries. It is the single best distribution story in any language ecosystem in 2026 and I am tired of pretending otherwise.
- A library that other teams in other languages will consume: Rust, exposed via a C ABI. This is, again, not a close call.
- An internal Lambda or Cloud Function: Go, because cold start still matters and Go’s binaries are small enough that nobody has to think about it.
I no longer feel guilty using both in the same organisation. The trick is being honest about which problem you have, not which language matches your team’s identity. Far too many teams choose their language because of identity (“we’re a Rust shop”) rather than fit, and that’s how you end up with a monorepo full of Rust config-loading scripts that take eight seconds to start, and Go services rewritten in Rust because it sounded cool, not because the math worked.
Pick the boring tool for the boring problem. Pick the precise tool for the precise problem. Be willing to be wrong; be willing to revise. That’s the entire thing. It just sounds less impressive than tribalism does.