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.
anyhow explicitly isn't designed for what you are trying to do here. It's designed to be the last link in the chain (and complementary to thiserror, not in competition). If you are using anyhow any deeper than your top-level binary crate, you are likely to be in for an unpleasant time.
Now that we have agentic coding I just write everything in Rust and couldn’t be happier. The struggle with rust was writing it, go was made so it was easy to write for mid level engineers. Now that we have agentic coding I’m not sure Go’s value prop holds up anymore
My rust services have been nothing short of amazing from a performance and reliability perspective
They treat it like it's JavaScript, falling back to using String/&str needlessly instead of making new types. They do ugly `static Mutex<Refcell<` a-la global JS variables for info sharing instead of working out the lifetimes to do it properly. It loves making functions infallible and then panic-ing within them and certainly I wouldn't use them for unsafe at all - they hallucinate safety comments which are in fact, totally unsound.
Of course these are all surmountable with an experienced developer to regularly step in and unfuck the code, but forcing them into 'harder' territory where every problem is not solved by a .clone() and an Arc<Mutex<>> means they will spend minutes 'thinking' about basic lifetime issues until I step in and add the missing `move` in a closure.
I'm not sold on Rust being a great language to use with AI unless the reason to use it is a lot more than just Rust being fashionable.
In practice, anything that makes it easier for humans to program also makes it easier for LLMs to program.
You also wont typically learn that the LLM is close to the limits of understanding your code base until after it has blown past it's own capabilities, leaving you with a mountain of code that you are not skilled enough to fix.
Java, C# are good choices as they tend to enforce a certain structure. Go, good because it's very readable even if you dont know the language.
C++, Rust are poor choices unless you are already a senior in that language.
I don't think the value prop has changed at all there. One day the AI gravy train will stop and people who used AI to punch above their weight will no longer be able to debug the stuff they built unless they put in the hard work of learning the language.
Nothing to worry about with Go in that respect because of how much it's been designed to be simple. Even the annoying err/nil checks you need to do all the time are in service of that simplicity. It gets old fast but it leaves nothing to the imagination.
Agents seem to have a better time with Go. Humans need to review the agents outputs and in general they have an easier time to do it with Go.
The minus side of Go is too simplistic GC. When latency spikes hit, there are little options to address them besides painful rewrite.
Interestingly, Rust has quite good failed compilation speed. That's almost good enough. The usual Rust experience is that it's hard to get things to compile, and then they work the first time.
https://uptrace.dev/blog/golang-memory-arena
These are all tools. Java used to have this all the time, and we (ex-java programmer) had ways around this until the JVM improved.
Or having Cranelift as default backend.
Yes but Rust has a lot more availability of libraries to do stuff as a result. Want to do anything ML or scientific? You at least have a route in Rust where you don’t with Go.
Rust has practically one error, it's the Error trait. The things you've listed are some common ways to use it, but you're entirely fine with just Box<dyn Error> (which is basically what anyhow::Error is) and similar.
pub async fn dataset_stats_handler(
Path(dataset_id): Path<String>,
Query(verbose): Query<bool>,
) -> impl IntoResponse {
...
}
With a route like: .route("/datasets/{dataset_id}/stats", get(dataset_stats_handler))
…the "dataset_id" path variable is parsed straight into the dataset_id arg, and a query string "verbose" is parsed into a boolean. Super convenient compared to Go, and you type validation along with it.Many other things to like: The absence of context.Context, the fact that handlers can just return the response data, etc.
What I don't like: Async.
type DatasetStatsQuery struct {
Verbose bool `form:"verbose"`
}
func DatasetStatsHandler(c *gin.Context) {
datasetID := c.Param("dataset_id")
var query DatasetStatsQuery
if err := c.ShouldBindQuery(&query); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// query.Verbose == bool
}}
This is actually a great example - what happens in that Rust version when the input parsing fails? Go makes it explicit. func Login(req LoginRequest, cookies Cookies, db *sql.DB) (LoginResponse, error) {
...
}
router.HandleFunc("POST /signup", fw.Wrap(Login))
It's just a wrapper.It also serializes/deserializes responses and handles both JSON and templates.
db is just a singleton-lifetime dependency, we often also have ctx, http.Request, http.Response, Cookie, which are request-time lifetimes.
I thought about open-sourcing it but most Golang developers seem to hate it with a passion, so I just gave up, haha.
The language design makes sense in the context of Oberon (1987), and Limbo (1995).
Now when there are so many options finally building on top of Standard ML, and Lisp heritage, having to settle with Go feels like a downgrade.
I code since 1986, if I wanted if boilerplate error handling, or having cost as the only mechanism to declare constant values, there have been plenty of options.
“Anyhow” just allows you to conveniently say “some Error” if you don’t care to write out an API contract specifying types of errors your function might spit out.
Though go certainly did a much better job than rust on the standard library front.
The entire point in Rust is that you wrap Error impls with other Error impls, or translate one impl into another using a match. I've found this is far more flexible and verifiable than most other languages, because if you craft your error types with enough rigor, you can basically have a complete semantic backtrace without the overhead of a real backtrace.
I use thiserror a lot to help with my impls. Notably, all it does is impl Display and Error. It's not a specific other paradigm because it basically compiles out, it's just a macro.
Anyhow is perhaps the closest one to another paradigm because it allows you to discard typed information in favor of just the string messages, but it still integrates well with Errors (and is one).
Not quite true. The unifying error trait is std::error::Error.
> pain when you have to pass them upward through a chain of calls
Kind of? You just make an enum with the various variants that need to be passed through and use the #[from] macro to generate the conversion code automatically.
It’s more characters than eg. A union type in Python or TypeScript, but it’s not much more.
Plus, it makes you think about your error design, which is important!
in rust say a function returns Result<T, E> so either the we get a result all an error how is that different from (int, err) in go?
do you not still need to handle the error?
in go you just return the error up to whatever the top caller is.
Missing error handling is checked at compile-time in Rust (lint-time in Go), and can be enabled for any struct or function (https://doc.rust-lang.org/reference/attributes/diagnostics.h...), not just `Result<T,E>`.
Returning an error to the caller in Rust can be done with a single character.
At this point, I can't imagine a scenario not to use Rust for writing a web API.
nit: Rust has three main Error implementations not systems. std::error::Error is a trait because it's left up to the user to provide project-specific error types, even if those types are anyhow::Errors. The system follows any implementation you use: ? syntax, .unwrap(), .expect(), handling, formatting, etc.
I've never had a problem using anyhow to pass errors up the call stack in any project I've used it in. What exactly is a pain?
But personally, I don’t mind Go at all. I’ve even begun to prefer it for some things. That may be Stockholm syndrome, though.
It's easy to write code that trivially eats memory. Plus any resources spent on it, are resources not spent on other cloud provider things.
Now there is a cult of rewriting everything in Rust. System level software? Yes. Web? I prefer not to.
Generally speaking there has to be a mechanism for optional handling of return values, in Go you can ignore everything (ew), you can use placeholders `_`, or you can explicitly handle things - my preference.
If you say "Well in C you have to handle the returns - I am not across C enough to comment, but I will ask you - Does C actually force you, or does it allow you to say "ok I will put some variables in to catch the returns, but I will never actually use those variables" - because that's very much the same as Go with the placeholder approach
edit: I am told the following is possible in C
trySomething(); // Assumes that the author of trySomething has not annotated the function as a `nodiscard`
(void)trySomething(); // Casts the return(s) to void, telling the compiler to ignore the non-handling
int dummy = trySomething(); // assign to a variable that's never used again
I welcome correction
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.
5% who write tools or other "infra" layer for the other 95% to work on top of maybe need that level of control over memory. It doesn't make any sense to me to sign up for that complexity unless you really really need it.
If youre not writing the code yourself and vibing away which I think most people generally are despite the disdain around here then why would you not choose the "more performant language" (I know that isnt necessarily reality but it is a common perception).
Go's managed runtime is less valuable when the LLM is perfectly happy to slap a bunch of stuff together for you to and approximate it and doesn't complain at all when writing async rust despite some of the rough edges.
And as mentioned in other comments, Rust slow compilation can be detrimental to LLMs + fast iteration speed. And it's not just speed, Tauri takes 20GB of disk space to compile. It's bonkers. This is npm/js ecosystem all over again but slower.
Another reason to pick Go if you're leaning on LLMs is the standard library. Often you can do more work with fewer dependencies.
I'd rather leverage world class engineers paid by Google to maintain dependencies for me than try my luck with half a dozen of 0.x crates. Plus stdlib APIs can (and are) versioned just like third party dependencies.
Most people are not doing that though. There's probably a good reason, and it applies to other languages too.
Rust had a "vibey" community long before vibecoding. In particular, it's long been fairly non-serious about yolo importing a bunch of crates to solve things (since the standard lib is small) which is kinda the same problem as having all those things just vibecoded. Either way, most projects weren't reading all of that other code!
Some of the problems Rust “solves” are problems you shouldn’t be having in the first place because we mostly write software that doesn’t need direct control over memory. Borrow checking isn’t something you want to have to deal with - it is something you have to accept when you have chosen to manage memory. That choice has a high cost that cost never gets paid off in most projects that could work just as fine with managed memory.
I’m a Go programmer, but this article reminded me that I should have more experience with Rust. From my perspective Rust seems a bit less practical. The standard library lacks support for cryptography, for instance. The compiler is slow, which is a productivity killer. Overall concurrency seems like a bit of an afterthought. Again.
What makes me want to try Rust in production are things like option types. Those would be nice to have in any language. Any issues that can be caught by the compiler are a plus. Getting rid of nil would also be a plus, but to be quite frank, I don’t experience that many nil pointer errors.
The author does nod to the static analysis tools for Go. Yes, they are not part of the compiler (for good reason), but they do a pretty good job in practice. So you get more than the compiler can promise at a fraction of the cost (measured in build time). That’s a much bigger deal for actual developers than we generally give it credit for.
Then there’s the stuff that makes me less convinced in terms of arguments. For instance the fact that Go didn’t have generics early on and that the standard library doesn’t use them. Generics were not as important as people thought they were. In practical reality. The fact that the standard library doesn’t make wide use of them is not a weakness, it shows restraint. They didn’t go overboard and prematurely plaster generics all over the place as soon as the language supported it. This is the kind of restraint you want to see. Remember how horrible Java was after everyone started abusing generics? A brief generation of software that was significantly worse, and less maintainable resulted from this exuberance. For the ultimate example of what happens when you give people every feature they wish for: look at C++. It´s not a very good language because it is many languages. Just because there are standards and recommendations doesn’t mean that all code magically gets rewritten to a narrower definition of the language. It means that we accumulate intermediate forms. I expect people who are interested in languages to understand these dynamics.
So in that sense it seems like a category error to try to look for crypto stuff in the standard library. Of course this brings the well known problem of "okay, but then which one should I use?". Nowadays this is largely solved by a few web searches and LLM queries, and people are quite helpful at https://old.reddit.com/r/rust/ .
Go was shaped by the needs of Google, Rust is a wildly successful amazing experiment in programming language and compiler design that really got out of hand :) (A bit like JavaScript! Or even C#! Or Python. Same growing pains (async/await!), but arguably on different levels.)
I wish TS had more of a runtime. The only thing I'm jealous of with regards to python is how seamlessly you can do JSON schema enforcement on HTTP endpoints. The Zod hoops are a constant source of irritation that only exists because the TS team is dogmatic.
Yes, it is much easier in Python because type annotations are reflected at runtime.
That's not really something I care much about. My beefs with Go are 90% about the syntax of the language itself, and it's weak (compared to Rust) type system.
When it comes to a managed runtime, for most tasks, I generally don't care if my language has one or not. For some tasks I do, but there are not many of those tasks, and so this question is mostly irrelevant to me when deciding Go vs. Rust.
I don't really get where you're seeing that the predominant Go vs. Rust debate is about the runtime. IME it's the subjective stuff about the languages themselves, and their ecosystems and communities.
> The Rust vs. Go slapfight is a weird and cringe backwater of our field.
::shrug:: I dunno, I mostly stay out of it and just use Rust, and I'm happy and avoid the drama. I've written a little Go here and there, didn't really like it, and moved on.
You don't need a garbage collector which is perhaps half of the Go Runtime when you're using Rust.
You can also bolt on a few crates and get ~95% of what you'd get from Go's runtime.
Go has the best runtime in the world. I'll give it that.
But this is not the only reason...
Sure, Go is better than Python in some things. But developers deserve the best. We deserve not to have to deal with Go’s quirks, idiosyncrasies and design mistakes.
True but unrealistic. Every language has warts, including Rust.
> concurrency — eliminating data races essentially, which we had before. Really gnarly bugs
> this is the one teams report most enthusiastically. The classes of bugs that survive go test -race and reach production (data races, nil dereferences, missed error paths) just don’t compile in Rust. Oncall rotations are typically very boring after a Rust migration. ...
> I hadn’t had to chase down a crash, or some weird multi-threaded race condition, or some of these other things which actually consumed a huge amount of my time before.
(They say at InfluxDb)
That's not a Rust vs. Go slapfight? Instead, sounds like a good judgement to me
I agree with the general sentiment though. Rust also has a lot of crates that are stuck semi-unmaintained at some 0.x version, often with no better alternative.
That is to say 0.x doesn’t necessarily mean unmaintained, it can also mean “I don’t want to have to think about how to version APIs / make guarantees about APIs). Eg reqwest is very widely used and actively maintained yet is still at v0.13.
Does any language, except like Java, exist with a standard library comprising matching that?
Also, keep in mind that Tauri itself is 14 crates, where each one shows up in your build tree.
https://github.com/tauri-apps/tauri/blob/dev/Cargo.toml
And Ratatui is 6:
[0] https://docs.python.org/3/library/sqlite3.html
Edit: counts are fair, that’s still hundreds unaccounted
Nobody has "solved" it, and I don't think that there will ever be one (never say never, though, right?)
For Go we rely on developers of libraries to adhere to the semver versioning scheme accurately, and we cannot "pin" versions (a personal bugbear of mine)
There is a couple of workarounds - using SHAs not unlike the git commit hash to provide a pseudo version, and, vendoring (which is a cache of known dependencies - which brings with it cache management problems)
I had the misfortune of having to use Python with a virtual env on the weekend - it did not end well, and reminded me why I migrated away from Python.
Look at Perl (cpan) Java (maven, gradle) Ruby (gems) Go (dep, glide, vgo, modules) Rust (cargo) Node (npm, yarn, etc)
OSes too Redhat (yum, rpm, etc) Debian (apt) Ubuntu (snap - god why????)
And so on
I see this sentiment a lot, and it doesn't match my experience at all.
In my decade-old bubble of using Python professionally, I've never had an issue with virtualenvs. The few issues I might've had with dependency resolution must be so far in the past that I don't remember. But that's not strictly about virtualenvs. Likewise, pip could be clunky, but we don't have to deal with it anymore.
My niche is mostly backend. Other Python niches must be considerably worse in this regard.
you can? that's why go.sum exists. you can also use the replace directive for more advanced scenarios.
And then you have httplib3 followed by httplib4.
In other words: I highly prefer the Rust approach.
It doesn't matter a lot whether I rely on the stdlib or another dependency to me.
It's a dependency after all.
People think just because it's the stdlib it's somehow better quality or better maintained, but these are orthogonal concepts.
In the end it depends solely on resources.
Sure, the stdlib may get more of these, but it may also grow fat and unmaintainable...
The c# guys at microsoft created an enormous stdlib, and the overwhelming majority of it is pretty good. The outliers being of course older stuff they've never really had time to upgrade. And they don't seem to be afraid to deprecate stuff, every major version brings a couple of minor breaking changes. But it all seems to work out just fine somehow
I did a lot of cryptography over the past couple of years. Go has that in the standard library. For the last decade and a half cryptography is something that every developer has to deal with at some point, and it NOT being the awful pain that it is in just about any other language, is a good thing. Sure, it does not contain every algorithm and mechanism in the world, but it contains everything you need for 90% of cases. That means that most of the time you don’t have to do the extra work of ensuring you have an out if the library you depend on should go away/bad, bugs will be fixed, people speak a common language and you don’t have to do twice the work in terms of risk assessment.
People keep forgetting that you have to evaluate these things in the real world. In practical real-world situations. The real world is not about what works in theory but what actually provides value for actual people working on actual projects.
Would it make sense to continue using Go for the frontend and doing only the backend in Rust for your user case?
Charitably, I think OP meant to say that in the rust project only four dependencies were added and that caused 400 transitive dependencies to be pulled. Adding the four Go equivalent will still result in 10x less packages being pulled.
It's a culture problem, Go authors prefer solutions that are self contained, rust authors embrace the culture that gave us left-pad.
But, at least in GP's case, it's not a stdlib problem. Not one solved by Go, anyway.
"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.The irony is that studies show LLM detectors have a much higher false-positive rate for non-native speakers [1]. If most of what you read stems from LLMs, you end up writing like an LLM.
[1]: https://hai.stanford.edu/news/ai-detectors-biased-against-no...
LLM writing has not been overly abundant for more than a couple years. I don't know where you got the idea that an entire generation of people have already learned to write like an LLM.
Specifically, I’ve recently used ChatGPT for legal/administrative writing where the AI seems to be trained on a large corpus and seems to know the conventions and vocabulary well; a lawyer who reviewed the work had important corrections. Before AI, I would have sought model filings and have had less success at emulating the genre. So it’s lowered time/cost somewhat but it takes lots of diligence. By default, current AI outputs seems intelligible but are still really far off the mark. I’ve found a structured interview is a good way to start rather than jumping into draft generation.
And it’s a good contrast with ‘just fcking use Go’ article he linked.
Go article is much more human. I love that and would choose a human centered language and human centered culture over LLM-centered everything every time
I guess I am just old
why scoff over someone doing assisted writing? i might age myself but kids back in the day would try to sound better by using synonym feature in ms word (or through web thesaurus) for their assignment essays. this all looks familiar to the same practice, now only made more accessible.
I feel the opposite, where AI hype is so extreme that merely someone pointing out an article may have had LLM involvement prompts a response like this. Someone incredulously painting people as ivory tower nose thumbers. If anything, it pushes me away from LLM writing more.
I also don't see how you can compare finding a synonym for a word to having your entire writing voice determined for you.
It is, if I may say that, _genuinely_ hard to use LLM assist and not make the text look like LLM generated. Even when I write an email in gmail and it gives its suggestions to make the text better, each one individually makes perfect sense, but when I click a few of them, the whole email now looks like AI slop, so I would normally undo the changes, going back to my imperfect hand-written non-optimized version.
Take this paragraph as example:
> Go got generics in 1.18, and they’re useful, but the implementation has constraints (no methods with type parameters, GC shape stenciling, occasional surprising performance characteristics). Rust generics monomorphize, each instantiation produces specialized code with zero runtime cost. Combined with traits, this gives you real zero-cost abstractions.
Every sentence says something. Every sentence is important and holds its weight. I would expect that kind of writing from very specialized books or papers, not from a blog post. Also, it makes the post harder (and more boring) to read.
I actually prefer that style of writing! (When it's not AI-generated ofc.) And I also try to use it in my technical blog posts. I usually re-read my drafts asking myself: "Does the reader actually care about this? Is this sentence adding something or is it just fluff?"
And actually I feel like AI text usually produces more fluff, or anyway I notice it more, but I see how it can make the result "robotic and boring".
The author of this article has what seems like it could be a relatively thriving consulting business, so he probably writes more to advertise his services than anything else. That kind of writing surely lends itself to a particular writing style, which is a non-insignificant chunk of the kind of writing that LLMs were trained on.
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.
Can you clarify how you're spending tokens on waiting? My understanding is that the LLM isn't actually necessarily doing anything while a build runs. The whole process end to end may take longer for sure (ignoring things like the compiler catching more errors, that's really hard to factor in) but how does that correlate to more tokens?
From what I've seen, Rust's strictness is actually a huge win for LLMs, as they get much better feedback on what's wrong with the code. Things like null checking that would be a runtime error in Go are implied by the types / evident in the syntax in Rust.
This is Silicon Valley fantasy.
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
The downside is that maybe it should fail sometimes when an idiomatic approach isn’t viable… instead it will implement something stupid that compiles and meets the request.
That's a more tractable problem then basically anything else around LLMs and programming. We're definitely getting more cores in the avg machine judging by roadmaps & leaks
var A string = "A"
type Foo struct { A string }
var a Foo
var b map[string]string
a = {A: "abc"}
b = {A: "abc"}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?
`nil` is not simpler than references and Option<T>. lack of enum is complicating my code. automatic type promotion is a hidden bug waiting to happen and preventing proper strong types, lack of `?` is making things verbose. struct tags look simple, until you realize they are hiding a ton of code and creating a ton of corner cases that you still have to manually check, and are completely nonstandard (hello json and `default`, `omitempty/omitzero` etc...). `nil` and interfaces? it took decades to recognize that Generics simplify things for the programmer, no Send/Sync like in rust makes concurrent code more error prone, etc, etc...
And that is without talking about the standard library, where "simple" somehow becomes having `url.Parse` that accepts everything without errors. http body `nil` vs `NoBody`. Who hasn't had to write the Nth implementation of a pipe between reader and writer? Apparently most libraries hear "simple" and think "dumb". We could go on for hours.
Golang is much easier to learn, and rust does remain much more complicated. I don't thing golang hit his target of "simplicity" honestly.
For cli tools, game engines, etc. certainly so. But what about monoliths? Do we have enough data to say Rust handles long-running monolith apps exposing web and other network services better than the JVM with its hot spot? I haven’t come to any stats on that matter, yet.
If you have some kind of super vague complicated patchwork of plugins that all contribute to processing, then the JVM seems to be the more convenient choice.
https://martinfowler.com/articles/mechanical-sympathy-princi...
That is a signal that person is lacking purpose in their job or life.
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.
As for error handling, this kind of enrichment is usually left to the caller (that is, the end application), with error libraries like anyhow where you can add arbitrary string contexts to an error. You would end up writing `Config::load(path).with_context(|| format!("Failed to load configuration file {path}"))?`.
i dont dislike rust at all (infact, its rustler interop with elixir/erlang is great), but the article reframing a bunch of intentionl design choices (that i would broadly argue as good design choices) in golang as shortcomings is so weird (gc, generics, error handling, etc.). especially so when they're framed in such a way to make error-prone go seem inevitable, or are directly comparing well-written rust and poorly-written go. take, for example, the section on data races. the article broadly classifies rust as data race free and golang as full of synchronization issues. and this is true if you only actually care about data races (not race conditions broadly), assume all of your rust is safe rust, and none of your golang uses any of the available solutions (atomics, synchronization primitives, channels, etc.) to data races. yes, go leaves much of the behaviour up to the programmer. this isn't a downside.
more egregiously, the article glosses over two of the biggest and, to me, most critical differences between the two language. first, go compiles FAST. i can write something and test it immediately, including stepping through the code. i dont need to context switch away from the task and can easily, and quickly, program fixes and changes and features. this is such a huge development gain that switching away from it would require an incredibly good reason. secondly, the package structure of rust offers a clear vector for supply-chain attacks. not that golang is perfect in that sense, but it has a ton of factors that reduce the likelihood, and if i'm being really picky about safety it's going to be a big consideration.
These percentages are from the JetBrains State of Developer Ecosystem Report 2024 on the question "Which programming languages have you used in the last 12 months?"[0].
I think a better datapoint would be the "Primary Programming Languages" in the 2025 report[1] where Rust sits at 4% and Go at 8%.
[0]: https://www.jetbrains.com/lp/devecosystem-2024/#KeDHWJ
[1]: https://devecosystem-2025.jetbrains.com/tools-and-trends
After writing web services, GUI apps and terminal apps professionally in Rust, I honestly struggle to see a use case for other languages.
It has a router, middleware, and uses AsyncRead, AsyncWrite for the request/responses.
I use this for my production applications and have found it much easier to work with than Hyper or Axium.
https://github.com/alshdavid-public/uhttp/blob/main/examples...
The API is largely complete but under the hood I have a few things that need doing. Open to contributions so please feel free to help out
Kind of funny when your Rust service runs on Kubernetes.
Even if you believe language X to be the bees knees, are you going to stop using it until everything below it in the computing stack has been rewritten in X? Of course not.
I am able to write rust on a moto g power (a cheap android smartphone) inside of termux, running on battery, in battery saver mode, and cached compile times for every single one of my projects is under 5s easily, if not faster. Fast enough that I don't notice it at all.
Even a "cold" compile was under 1 minute for me, and I have a decent amount of deps.
I guess my projects are fairly small compared to others though so idk.
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.
Maintenance is a big win for Go imho - that you can go to code you wrote a year or more ago - and jump right back into it, with little-to-no re-learning curve. The syntax is not providing cover for complexity bombs, and the tools keep the workflow simple and quick.
How is it with Rust ? Does one's own old code remain maintainable ?
> "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?
And what the article complains about is by design, not a bug. It is a tradeoff made to avoid bloat. In any case, given the future possibilities, I'd bet on Go.
If anything, the language is just slower to evolve because every language change means the tooling needs to catch up. And now llms would have to catch up. ChatGPT is still using Go 1.23 for instance...
C++ also forms a natural barrier to entry.
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.
If you have a mutex on a structure, linters such as are packaged into Goland will catch oversights quite effectively.
If you are using fancier concurrency structures, you should consider channels instead.
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...
As you note it's just pain with no gain to properly hide them. Users can't readily work around bugs or extend functionality.
It seems like you lose a lot (automatic memory safety, simple language, easy concurrency) and gain very little.
Now Rust is the new Go.
I find that very confusing.
For example, I can transmit the response to the client and then free the memory afterwards so they're not kept waiting.
But Go to Rust???
It does not make any sense.
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.
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...
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...
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.
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.
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.
Lmao so not an equivalent then? Standard glibc malloc, which is default in rust, will also similarly degrade albeit for different reasons.
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!