{"id":494,"date":"2026-07-23T05:02:07","date_gmt":"2026-07-23T05:02:07","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/rust-vs-go-2026-the-heuristic-i-actually-use\/"},"modified":"2026-07-23T05:02:07","modified_gmt":"2026-07-23T05:02:07","slug":"rust-vs-go-2026-the-heuristic-i-actually-use","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/rust-vs-go-2026-the-heuristic-i-actually-use\/","title":{"rendered":"Rust vs Go in 2026: The Heuristic I Actually Use"},"content":{"rendered":"<p>Confession: I&rsquo;ve started this post three times over two years and thrown it away every time, because every version turned into a benchmark table and benchmark tables are the least useful part of this question.<\/p>\n<p>Here&rsquo;s what actually happens. Someone asks &ldquo;Rust or Go?&rdquo; What they mean is &ldquo;I have a thing to build and about six weeks, which one won&rsquo;t ruin my life.&rdquo; Nobody is choosing a language for the microsecond difference on a JSON parsing benchmark. They&rsquo;re choosing what their team will be typing at 11pm during an incident.<\/p>\n<p>I write both. I ship Go for most services and Rust for a smaller set of things, and after a few years of that the decision has stopped feeling difficult. So let me tell you where the line actually falls for me, and where I got it wrong first.<\/p>\n<h2 id=\"the-performance-question-is-real-but-boring\">The performance question is real but boring<\/h2>\n<p>Rust is faster than Go for CPU-bound work. That&rsquo;s true, it&rsquo;s consistent, and it mostly doesn&rsquo;t matter for the thing you&rsquo;re building.<\/p>\n<p>The reason is the garbage collector. Go has one, and it&rsquo;s a good one: <a href=\"https:\/\/go.dev\/blog\/ismmkeynote\" rel=\"nofollow noopener\" target=\"_blank\">the Go team&rsquo;s GC design<\/a> targets sub-millisecond pause times, and in normal service workloads you will not notice it. Rust has no GC, so it has no pauses at all, and it has no runtime allocating things behind your back.<\/p>\n<p>Where that gap shows up in practice:<\/p>\n<ul>\n<li>Tail latency under memory pressure. Go&rsquo;s p99 wobbles in ways Rust&rsquo;s doesn&rsquo;t.<\/li>\n<li>Memory footprint. A Rust service doing the same work often sits at a third of the RSS.<\/li>\n<li>Anything with tight per-item costs at high volume: parsers, proxies, encoders, simulation loops.<\/li>\n<\/ul>\n<p>Where it doesn&rsquo;t show up: literally any service where the request spends 40ms waiting on Postgres. Your database is the bottleneck. Rewriting the 2ms of application time into 0.4ms is not a project, it&rsquo;s a hobby.<\/p>\n<p>I say this as someone who once rewrote a Go ingestion worker in Rust to fix a latency problem that turned out to be a missing index.<\/p>\n<h2 id=\"where-rust-actually-earns-it\">Where Rust actually earns it<\/h2>\n<p>The case for Rust isn&rsquo;t speed. It&rsquo;s that the compiler refuses to let a whole category of bug exist.<\/p>\n<p>The borrow checker means no data races in safe code. Not &ldquo;fewer&rdquo;, not &ldquo;unlikely&rdquo;. The compiler will not build a program where two threads can mutate the same value without synchronisation. That guarantee is worth an enormous amount on anything concurrent and stateful, and there is no equivalent in Go.<\/p>\n<p>Go&rsquo;s story here is different. It gives you cheap goroutines and channels and then trusts you:<\/p>\n<pre><code class=\"language-go\">counter := 0\nfor i := 0; i &lt; 100; i++ {\n    go func() {\n        counter++ \/\/ data race, compiles fine, runs fine, wrong answer\n    }()\n}\n<\/code><\/pre>\n<p>That builds. It runs. It gives you a number that&rsquo;s wrong in a way you&rsquo;ll notice in about three months. <code>go run -race<\/code> catches it if you remember to run it and if the race happens during your test.<\/p>\n<p>The Rust version doesn&rsquo;t compile:<\/p>\n<pre><code class=\"language-rust\">let mut counter = 0;\nfor _ in 0..100 {\n    std::thread::spawn(|| {\n        counter += 1; \/\/ error: closure may outlive borrowed value\n    });\n}\n<\/code><\/pre>\n<p>You&rsquo;re forced into an <code>Arc&lt;Mutex&lt;i32&gt;&gt;<\/code> or an atomic, and the compiler makes you say which. Annoying on day one. Excellent on day two hundred.<\/p>\n<p>The second thing Rust gives you is error handling that you can&rsquo;t ignore. A <code>Result<\/code> that you don&rsquo;t handle is a warning at minimum, and with <code>#[must_use]<\/code> semantics on most APIs, ignoring it is deliberate. Go&rsquo;s <code>if err != nil<\/code> is fine, but nothing stops you writing <code>val, _ := doThing()<\/code> at 2am, and I have.<\/p>\n<p>Enums are the underrated part. Modelling state as a Rust enum with data attached, then matching exhaustively, catches design mistakes at compile time that Go&rsquo;s interface-and-type-switch approach finds at runtime. I built a payment state machine this way and the compiler found four transitions I hadn&rsquo;t thought about.<\/p>\n<h2 id=\"where-go-actually-earns-it\">Where Go actually earns it<\/h2>\n<p>Compile times. I&rsquo;m putting this first because it&rsquo;s the one people dismiss and then feel every single day.<\/p>\n<p>A Go service builds in a couple of seconds. A Rust service with a moderate dependency tree takes a minute or two on a clean build, and incremental builds are decent but not Go-fast. Over a working day of small changes and re-runs, that difference changes how you work. Go keeps you in a tight loop. Rust nudges you toward thinking harder before compiling, which is sometimes good and sometimes just waiting.<\/p>\n<p>Then: onboarding. Go&rsquo;s spec is small enough that a competent developer from any background is useful in a week. Rust&rsquo;s ownership model takes longer. Not &ldquo;hard&rdquo; longer, but weeks-not-days longer, and the <a href=\"https:\/\/doc.rust-lang.org\/book\/ch04-00-understanding-ownership.html\" rel=\"nofollow noopener\" target=\"_blank\">ownership chapter of the Rust book<\/a> is genuinely the wall people hit. If you&rsquo;re hiring, or if the code needs to be maintainable by whoever comes next, that&rsquo;s a real cost with a real number attached.<\/p>\n<p>Async is the third one. Go&rsquo;s concurrency is built into the runtime; you write blocking-looking code and the scheduler deals with it. Rust&rsquo;s async is a library-level thing built on futures, which means you pick a runtime, you deal with <code>Send<\/code> bounds on your futures, and you occasionally end up in a fight about lifetimes inside a spawned task. It&rsquo;s got much better. It&rsquo;s still more moving parts than <code>go func()<\/code>. I went into which runtime I&rsquo;d settle on in <a href=\"https:\/\/abrarqasim.com\/blog\/rust-async-runtime-comparison-2026-the-one-i-actually-ship\/\" rel=\"noopener\">my Rust async runtime comparison<\/a>.<\/p>\n<p>And the standard library. Go&rsquo;s <code>net\/http<\/code> is production-grade out of the box. <code>log\/slog<\/code> is in the standard library now. <code>encoding\/json<\/code> is there. For a normal HTTP service you can go a long way on the standard library alone, which keeps your dependency tree small and your supply chain narrow.<\/p>\n<h2 id=\"the-heuristic-i-actually-use\">The heuristic I actually use<\/h2>\n<p>Go, unless there&rsquo;s a reason:<\/p>\n<ul>\n<li>HTTP and gRPC services, CRUD, anything database-shaped<\/li>\n<li>CLI tools that need to be one static binary<\/li>\n<li>Anything a team of mixed experience will maintain<\/li>\n<li>Anything where I&rsquo;ll be shipping several times a day<\/li>\n<\/ul>\n<p>Rust, when one of these is true:<\/p>\n<ul>\n<li>The work is CPU-bound and the CPU is the actual bottleneck (measured, not assumed)<\/li>\n<li>Memory footprint matters, because I&rsquo;m paying per MB or running on small hardware<\/li>\n<li>The concurrency is complicated enough that I want the compiler policing it<\/li>\n<li>It&rsquo;s a library other people will call, especially across an FFI boundary<\/li>\n<li>Correctness in a state machine matters more than shipping speed<\/li>\n<\/ul>\n<p>The honest summary is that Go is my default and Rust is my choice for specific problems. That&rsquo;s not a diplomatic non-answer; it&rsquo;s just what my repo list looks like. Most things are services. Services are IO-bound. Go is very good at IO-bound services.<\/p>\n<h2 id=\"the-rewrite-question\">The rewrite question<\/h2>\n<p>&ldquo;Should we rewrite our Go service in Rust?&rdquo; Almost always no.<\/p>\n<p>The gain is usually latency and memory. The cost is the rewrite plus every bug you reintroduce plus a team that&rsquo;s slower for a quarter. If your service is IO-bound, you&rsquo;re spending months to optimise the 5% of the request that isn&rsquo;t waiting on something else.<\/p>\n<p>What works better: leave the service in Go, find the one hot component, and make that a Rust library. Both languages have decent FFI. You get the win where the win exists and you don&rsquo;t burn a quarter.<\/p>\n<p>Where I have gone all-Rust from the start, it&rsquo;s been things like a log-processing pipeline chewing through a large volume of lines, where the per-line cost genuinely dominated. That one was worth it. I laid out the backend shape I ended up with in <a href=\"https:\/\/abrarqasim.com\/blog\/axum-sqlx-rust-backend-setup-i-actually-use-in-production\/\" rel=\"noopener\">my axum and sqlx setup<\/a>.<\/p>\n<h2 id=\"something-to-try-this-week\">Something to try this week<\/h2>\n<p>If you&rsquo;re a Go developer curious about Rust, don&rsquo;t start with a service. Take one small CLI tool you already have and rewrite it. A hundred lines, no async, no web framework. You&rsquo;ll meet ownership and <code>Result<\/code> without also fighting a runtime and a framework at the same time, and you&rsquo;ll know within a weekend whether the model clicks for you.<\/p>\n<p>If you&rsquo;re a Rust developer wondering about Go, write a small HTTP service using nothing but the standard library. No framework. It&rsquo;ll take an hour and it&rsquo;ll explain the appeal better than any comparison post, including this one.<\/p>\n<p>Either way, pick based on what the thing is, not on which language is winning the argument this month. I&rsquo;ve built production systems in both and I&rsquo;ve got no interest in defending either one; if you want to see what that looks like in practice, <a href=\"https:\/\/abrarqasim.com\" rel=\"noopener\">most of my work<\/a> is somewhere in the middle.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Rust vs Go, decided by what you are building rather than benchmarks: where the borrow checker pays off, where Go&#8217;s compile times win, and when not to rewrite.<\/p>\n","protected":false},"author":2,"featured_media":493,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"Rust vs Go, decided by what you are building rather than benchmarks: where the borrow checker pays off, where Go's compile times win, and when not to rewrite.","rank_math_focus_keyword":"rust vs go","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[45,142],"tags":[49,212,46,47,566,64,565],"class_list":["post-494","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming","category-rust","tag-backend","tag-concurrency","tag-go","tag-golang","tag-language-comparison","tag-rust","tag-systems-programming-2"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/494","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=494"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/494\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/493"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=494"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=494"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=494"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}