A migration guide from the Rust consultancy corrode reached the front page of Hacker News this week (458 points, 461 comments). It walks through moving a Go service to Rust, and the thread split the way these threads always do: half the room wants to rewrite everything, the other half calls the rewrite a waste. Both camps miss the same point. The decision is rarely about the two languages in the abstract. It is about the one specific service sitting in front of you.
I run a site that compares developer tools, so I read a lot of "X vs Y" content. Most of it argues in the abstract and lands nowhere. This guide is worth your time because it grounds the comparison in what changes when you move real code, and it stays honest about the parts that get worse. Here is the grounded version, checked against a few independent sources.
What gets better
Error handling stops being a convention and becomes a type. Go returns a (value, error) tuple and trusts you to check it. The compiler will happily build a function that ignores the error, which is why teams bolt on linters like errcheck. Rust returns Result, and the compiler refuses to let you touch the value until you handle the failure path. The ? operator threads errors up the stack, and with a #[from] attribute it converts error types for you. The boilerplate you write by hand in Go is structural in Rust.
The nil pointer goes away. A nil dereference in Go is a runtime panic, and linters catch it probabilistically at best. Rust has no null. Absence is Option, and the compiler makes you open the box before you read what is inside. For a service that has paged someone at 3am over a nil map access, that property alone earns the evaluation.
Data races turn into compile errors. Go's -race detector is good, but it only finds races that happen to run while your tests run. Rust encodes thread-safety in the Send and Sync traits, so the compiler rejects unsafe sharing before the binary ever executes. The Go habit of a sync.Mutex guarding a map becomes Arc>> in Rust. More verbose, yes, and the verbosity is the safety.
The resource numbers are real, with one caveat. The corrode guide cites a 20-40% CPU improvement and a 30-50% memory reduction across production migrations, most of it from dropping the garbage collector and its P99 latency jitter. Read those as the consultancy's reported range, not a promise for your workload. The direction held across every independent comparison I checked.
What gets worse
Function coloring is the tax everyone underestimates. In Go you write a function and call it; goroutines never change a signature. In Rust, async fn and .await split your code in two, and synchronous code cannot call async code without an executor. Every source I read, including people glad they switched, named this as the single biggest day-to-day regression coming from Go. It is the thing Go developers miss most after the move.
Compile times go from instant to minutes. Go's near-instant build is part of how the language feels. A clean release build of a medium Rust service can take minutes, and the guide flatly calls that "a real downgrade." If your team's loop depends on fast rebuilds, measure it before you commit, not after.
The first service takes three to six times longer to ship. The borrow checker is a wall and you hit it in week one. The honest estimate, repeated across sources, is that developers new to Rust stay meaningfully less productive for three to six months. Budget formal training instead of learning while shipping, or the timeline slips in silence.
The ecosystem still has holes. Rust's crate registry is large, but Go owns a few backend-adjacent niches outright: Kubernetes operators, several cloud SDKs, particular database drivers. The guide warns that migrating teams often hand-roll one or two core libraries themselves. That is a real line item, not a footnote you can wave away.
The decision that counts
The useful sentence in the corrode guide is the one most rewrite-everything threads skip: not everything should be migrated. It recommends keeping Go for CLI utilities, Kubernetes tooling, and any service where team velocity matters more than absolute correctness guarantees.
So the calibration comes out clean. Migrate the service where a nil deref or a data race becomes a production incident and the latency floor is a contract: payment paths, stateful coordinators, anything carrying a tight P99 SLA. Keep Go for the glue: internal tools, operators, the service whose whole value is that a junior shipped it on Friday afternoon. The two languages are not competing for the same job. The migrations that go badly are the ones that pretend they are.
If you are weighing the move, run the smallest honest experiment you can. Pick one self-contained service, port it, and time the work end to end. Track the compile times, the review friction, and how long the borrow checker holds you up. The numbers you measure on your own code beat any blog post on the internet, this one included.
A migration is a bet on where your incidents come from. If your pages are nil derefs, data races, and GC pauses, Rust moves those failures from runtime to compile time, and that trade pays for itself. If your pages are missed deadlines and onboarding drag, Rust adds to the column you are trying to shrink. Name your failure mode first, then pick the language that kills it.
If you are setting up either side of this, I keep practical configuration kits for both: a Go Development Cookbook and a Rust Development Cookbook, each with CLAUDE.md rules, editor hooks, and project patterns wired in. They will not make the borrow checker friendlier, but they cut the first-day setup friction so you reach real code faster.
Sources
- corrode, "Migrating from Go to Rust": https://corrode.dev/learn/migration-guides/go-to-rust/
- JetBrains RustRover blog, "Rust vs Go: Which One to Choose": https://blog.jetbrains.com/rust/2025/06/12/rust-vs-go/
- LogRocket, "Go vs Rust: When to use Rust and when to use Go": https://blog.logrocket.com/go-vs-rust-when-use-rust-when-use-go/
More from-real-use language and tooling tradeoff writeups at tools.thesoundmethod.me — decisions made from shipping, not benchmarks in isolation.
Top comments (0)