Every language has its zealots but I have run into more Rust zealots lately. That makes sense, of course, since Rust’s trajectory and growth is amazing.
That being said, it is frustrating when I get asked (or scoffed at) for writing something for fun in a non-Rust language. It’s weird, and it’s happened more than once.
Regardless of your favorite programming language, if someone is excited to tell you about something they worked on…just be happy for them.
Completely off topic, but I feel like theres a pressure on tech forums to be happy for one another. But often times I do not feel happy for my fellow commenters, but quite angry at them.
Totally agree. I had an experience recently where a younger guy I know started taking a course online in python and databases because he really wants/needs to find a better job. And he called me worried because he talked to some in-law of his who was telling him he should be learning X, Y, Z languages and tools, so my buddy thought he just wasted money and time on these courses. I was able to calm him down, and have been spending time showing him linux command-line stuff to help him figure out his own environment issues (always the hardest part when you are starting, right?! even now!) which made him feel way more competent than he had before. So, definitely just be happy for people, and be encouraging when they are learning new things and encourage to not give up.
I went to a Rust meetup a few times and had this a lot. I’m much more a product builder than a programmer so my mentality is often “get it in front of people asap” so I’m usually hopping between languages to use whatever is the right tool for the job. Meanwhile, those who scoffed at me would be discussing compiling complex Rust code to the frontend just to avoid React for the simplest tasks. It was an odd experience but fun nonetheless, I’m happy for them!
My guess is that “the number of times you are asked” is a significant contributor. For the person asking the question, that’s a new info why not Rust for this particular project. For the person answering this same question for the tenth time there’s no information sharing, just tiring repetition.
Regardless of frequency it’s also just…kinda rude.
“I’m making broccoli cheese soup tonight.”
“Why not barley vegetable? It is so much healthier, after all.”
It’s vaguely presumptuous and condescending, implying that the person doesn’t know what’s good for them or that they don’t know their own tastes and skills.
Hm, not sure it’s rude in and of itself: I think it’s generally ok to offer people information you consider helpful, even if not explicitly asked about that. This helps to avoid the situation where you don’t know that there’s something you don’t know.
Or, more precisely, this depends on interlocutor of course: some people in some context loathe receiving unsolicited advice, some like it sometimes. But if you don’t know for sure (this is not a repeated interaction), you need to choose something, even if that’s asking to ask.
So, in general, I think politely asked “why not X?” is a great way for at least one participant to learn something.
Where this gets tricky is 1:N interaction, where you have a bunch of folks independently asking the same stupid question politely!
This helps to avoid the situation where you don’t know that there’s something you don’t know.
In general, I’ve had success approaching this by (EG) “Oh cool! I often make a barley-vegetable soup which I really like; let me know if you want to trade recipes sometime”.
I think the important differences are:
Communicates that information / help is readily available should they wish to avail themselves of it (‘Why not X?’ is not an offer of help)
Avoids implying the other party is ignorant of X (for all you know, their knowledge/expertise may be vastly superior to yours)
Avoids demanding the other party do the work to explain themselves (offering to contribute comparable effort instead of dropping a question that’s easier to ask than to answer)
“Oh that’s nice I actually do this other thing” often still comes off as pushy, and engineer types do it A LOT. A more socially well adjusted (hypothetical) person would probably say “oh cool I’ve only had the bad canned type of broccoli-cheese, how do you blend in the cheese?” (something that demonstrates an interest in what they’re doing). Then after they have shared a bit more maybe say “I’ve been into barley-vegetable, yours seems delicious but I’m scared of the calories” even then it can seem pushy and is better to err on the side of not sharing your stuff because people always overestimate the value to the other person of their shit. ESPECIALLY engineers lol.
In the case of someone doing a project in C in 2023, it’s safe to assume they are aware of the purported benefits of Rust. “Oh cool what made you decide to do it in C” would be a pleasant way to ask for information if they are actually curious.
I wonder how much of what you call “socially well adjusted” is specific to American culture. In my central European country everyone seem to be much more direct and open to criticism 🤷
“Oh cool what made you decide to do it in C” would be a pleasant way to ask for information if they are actually curious.
Yeah I wish I had more of this kind of discussion. I’m actually quite interested in Rust, I just kind of want to learn stuff from bottom-up. Seeing the benefits of Rust, Zig etc. will be easier when I have dabbled with C and C++ at first.
I’ve heard people generally don’t like either of these kind of responses, because they’re taking the conversation (A is eating delicious soup) and making it about yourself. The “correct” answer is “Oh, cool.” maybe followed up by “What made you go with broccoli cheese?”. People love the opportunity to talk about the things they enjoy, and they love the opportunity to ask about the things others enjoy.
Sometimes it’s rude, but when I say I am doing this as a learning project, I get the whole “But Rust is much safer you should learn that now since it’s hot” spiel. Maybe I read into it too much.
Sorry, I didn’t mean to make you feel this way! I didn’t want to question how it makes you feel, I was just wondering if there is something specific that you are able to pin point (it’s fine if there is no such thing). People tell me I’m often perceived as too direct and you could call me rust fanboy so I was hoping to learn something here.
It’s things like how enjoyable is writing, building, and hacking with a language, that I care more for, than themes like scalability, enterprise use, or performance that the original post describes.
go doc | grep is a fucking experience. Some time ago I was editing a Go program that needed to access the file system in a general manner. I know that package os can provide an fs.FS view for the directory name I have at hand, but I don’t remember the name of the function in package os that does this. So I switch to a terminal and issue:
% go doc os | grep FS
func DirFS(dir string) fs.FS
%
Then I use os.DirFS in the program.
The Go toolchain is closer to feeling like a Unix thing. Each build error from the go command produces exactly one line of output. None of the go tool commands write colors in their output. But I’m glad that cargo --color never exists, and I alias cargo this way. :)
And on the hackability factor, Go programs tend to write themselves with just the standard library alone. For example, when I wrote a small static website generator binary some years ago, the program naturally hacked itself with only standard library imports and a single external markdown package. I wonder if it would have been as fun hacking it with Rust - vetting external packages and what not.
+1 re: stdlib. The ability to solve many non-trivial problems in Go (from web applications to low-level networking) with 0 dependencies is its killer feature.
And on the hackability factor, Go programs tend to write themselves with just the standard library alone.
I don’t know how and where you use Go, but if dig bellow the surface and see how most companies use it… oh boy… are you in to a gigantic unpleasant surprise.
Right now the system is full steam ahead in the direction of Java-esque corporatey BS. Don’t believe me? Head up to github, check a random go project and check the dependencies and the amount of code.
Almost all go codebases I have seen recently already do things like never, under any circumstances, referencing to a function directly. All calls go via an interface. The very same fundamentalism deprived of reasonable critical thinking that plagues the java world is already well establish among the Go community.
The meme is out that “Go is a better java”, I have seen that written in this very website a couple of times already, last week alone. People say this with a serious tone and don’t realize that they are steaming ahead full throttle towards the iceberg. They think it is a good thing.
Gather 100 Go developers and ask them to write a simple http service. You’ll be lucky if you find one that doesn’t immediately import some framework, router, whatever to handle the requests, rather than just using the stdlib. Same goes for things like reading environment vars, simple string interpolation, outgoing requests, etc.
I remind you that java (and other languages) didn’t lack such functionality in its standard distribution. To this day, after touching probably thousands of java HTTP services, I am yet to find one where the author simply used the httpserver included in Java SE distribution.
It’s naive to believe that go will be (is?) different in this regard.
Almost all go codebases I have seen recently already do things like never, under any circumstances, referencing to a function directly. All calls go via an interface.
I’m sorry that this is your general experience. I have also seen it in the wild. But I don’t agree that it characterizes “almost all Go codebases” in general. It is not difficult to write bad Go code like this, but it is also not inevitable that all Go code will end up like this, either.
“Go is a better Java / C#, while Rust is not”. (Emphasis mine)
As a Go programmer, I wish such drivel was at the top of the article, rather than in the conclusion. It would have saved me the time I wasted reading it.
Huh? From where I stand, Go does 100% look like a better(both in the sense “they solve similar problems” and in “actually better” sense) Java, up to repeating generics trajectory. It is similar to Java because
targets the same “general & server-side programming” niche
has the same original design goal of being a simpler language
has a close perf profile of a statically typed GC language
places a lot of emphasis on the tool chain, rather than the language
It directly improves upon Java by:
using fast (optionally cross) compilation to a statically linked native binary. This:
solves “easy to run” goal of Java better than Java’s approach with VM
removes dynamism from the runtime, which historically became a major source of complexity in the Java ecosystem.
adding first-class support for important performance optimizations: value types and stackful coroutines, something Java also now tries to add
simplifying the toolchain by packaging it up in a single-binary-interface: formatter, profiler, package manager, etc, are readily available as go commands.
C# also followed “better Java” trajectory, but it did so by adding stuff, not by removing stuff. I don’t know about C# enough to confidentially say that Go is better, but it surely does look like a defensible position.
And Rust is most definitely not a better Java, as the domains are wildly different.
I feel people think of Go as a better Java / C#, but it isn’t. And that false belief results in a lot of Go projects that should never have been Go projects, and suffer for it.
It’s fun and easy to meme on Java, but it succeeds as an application language and runtime because it’s actually good for building applications.
I definitely do maintain such a belief! I’d be very curious to hear specific arguments why Java would be a better language, and some specific examples of go programs which should have been Java programs. Of the top of my head:
if you are building a platform with dynamically loaded plugins (think Eclipse or IntelliJ), you obviously can’t do that in Go, you need a fairly dynamic runtime!
somewhat related, if you are building a desktop GUI, sucks to be you because the ecosystem is in general a train wreck, but Java is definitely among the least bad options here.
there’s a line of argument that Java is faster because JIT, generational GC and many runtime knobs. This in turn is countered by value types. Benchmark results do not seem to imply that Java is in general clearly faster.
but it succeeds as an application language and runtime because it’s actually good for building applications.
It absolutely was the best “general purpose” language in 2000-2015. I think it is not that today.
This is why asking the question is rude. Somebody wants to show everybody an hilariously shaped rock they found gardening, and neckbeards in the room start arguing about what colour the handle of your shovel should be painted, and how blue is obviously better for digging hard ground while green is far superior for moving large volumes of leaf litter.
I don’t understand this comment (like, literally don’t understand relation between metaphor and the current conversation thread). Given upvotes, it probably is useful for me to understand, could you elaborate a bit?
It seems to me that the core implication (which may be exaggerated for effect) is that programming languages are as interchangeable as colors of paint, and practical differences between them exist only in people’s heads. (I don’t think I agree with that implication.)
Not at all. The core implication is that a) it’s ok to allow an entire conversation to run its course without interrupting everybody in the room to tell them why your favourite programming language is the best, and b) the arguments themselves are meaningless shouting about orthogonal points, and usually only matters of taste or opinion dressed up and presented as if they were proven facts.
Somebody writes a post about how they’re sick of nerds hijacking every single discussion to shout past each other about their current favourite programming language, and the comments in here instantly became exactly that; Nerds ignoring the topic and blasting the room with “facts” they made up justifying why their current favourite programming language is the best.
The Java ecosystem is closer to Django / Rails than anything in the Go ecosystem. If you want to make a new web app there are tons of tried and true Java web frameworks. The Go ecosystem is more a collection of libraries.
I also think Java the language is more expressive for building complex business logic abstractions. I’ve worked on a Go project where adding workflows required updating no less than 5 different boilerplate files. Could a different design have been better? Absolutely. But like all business logic it grew organically, and typical Go code doesn’t grow and adapt to new complexity without a lot of refactoring.
For more “systems-y” things, I would definitely prefer Go. For example, a scalable websocket server for an application otherwise written in Python. I’m also a big fan of Caddy. And I think Go makes an excellent language for command line tools. On the spectrum of C to Python, Go falls closer to C and Java falls closer to Python.
Thanks, this is an update for me: it does seem believable that, if you are building a rich web app with heaps of business logic (which is a common case), than you might get more mileage of JVM libraries and expressiveness(my understanding is that’s stuff like annotation processors, reflection, and other things which could make Java feel like Python, not the the nuts and bolts of more OO design)
if you are building a platform with dynamically loaded plugins (think Eclipse or IntelliJ), you obviously can’t do that in Go, you need a fairly dynamic runtime!
Thanks, didn’t realize Go has that! Though, it’s a thin wrapper over dlopen, and unix only, so probably not a good idea to try to build an Emacs out of that.
if you are building a platform with dynamically loaded plugins (think Eclipse or IntelliJ), you obviously can’t do that in Go, you need a fairly dynamic runtime!
The whole hashicorp ecosystem disagrees vehemently with you.
On the other hand, the hashicorp ecosystem is the only example of plugin architecture in go I can think of. But then, I don’t know a lot of go stuff.
From a quick look, it looks like hashicorp plugins are separate processes using IPC for communication. For this bullet, I had “shared memory plugins” in mind. It’s an open question whether shared memory plugins are a good idea at all. For example, in VS Code plugins communicate with the editor’s core via IPC. At the same time, plugins can communicate amongst themselves via shared memory, as all plugins are in the same process. Fuchsia is heavily IPC based, and that does seem like a good “from first principles” approach. And WASM with interface types confuses the picture even more, as it really blends IPC vs shared memory boundary.
And WASM with interface types confuses the picture even more, as it really blends IPC vs shared memory boundary.
I think it’s new enough that there are no mature applications with a WASM plugin architecture, but to me it’s such an obvious fit that I can’t wait to try it.
It’s not only new, it’s not really sufficiently there yet. You really need something like wasm components to get into the standard to describe meaningful plug-in APIs. Without this, plug-ins reduce to C ABI, which sucks.
In other words, a plug-in is a box with a computational device which does something, plus a bunch of toggles on top of the box as an interface. Today, WASM is great for describing the contents of the box, but it barely has anything on the interface side.
The original article also had emphasis on that word (italic). Maybe that was supposed to mean something like that it’s a newer language that competes in the same space as Java / C#, while Rust is a better C++ instead. I’m the post author btw.
Having worked professionally with both Rust and Go, I don’t agree that Rust has “worse overall productivity” than Go; indeed, I’ve spent a huge amount of time tracking down strange concurrency issues, poorly documented interfaces, and tooling issues with Go, and that hasn’t been an issue for me with Rust. That said, I worked with Go in the context of Docker internals, so that might be the root of the problem rather than the language itself.
100% yes, the more I use Go the more I discover it’s just a syntactic wrapper around C. Trying to maintain a project with transient dependencies is a nightmare (I’m looking at you /x/stdlib). It’s type system is so rudimentary that I struggle to parse json in a fashion that doesn’t have me writing functions like doThingInt and doThingDouble. And don’t even TRY to write any statistical / numerical modelling software with it, since it’s type system can’t even do basic conversions like Int -> Int64 automatically / intuitively.
And don’t even TRY to write any statistical / numerical modelling software with it, since it’s type system can’t even do basic conversions like Int -> Int64 automatically / intuitively.
Those conversions are performance-sensitive; doing them automatically means unpredictable memory layout changes.
Personally, that kind of performance tweaking is totally irrelevant to the software I write, but if I were writing numerical modelling software I’d definitely want to know which integer type I were working with very early on.
I’ll provide a more concrete example. Let’s say I’m trying to model ocean flows, I’ll use a grid system where temperature / inflow / outflow are variables within the grid. The data type is extremely important, but once it’s defined in a structure, type conversion should be automatic. For different parameters, we’ll want to precisely control the floating point precision / size etc, but when doing numerical computations, we shouldn’t care what type the values are! We should be able to write a function that automatically performs the conversions while computing AND it should be able to perform the last conversion to the output variable that we’ve defined a float/int type for.
Edit: Floating point issues removed for brevity :)
Right - I had assumed that for numerical computations you would want to get the data into matching types ahead of time, instead of doing the conversion inside the loop. However, I can see that would sometimes involve an unacceptable memory use overhead.
I think this is a matter of what you’re working on. If the performance differential (in memory or CPU use) between Go and Rust is not that important, then bothering with lifetimes/the borrow checker seems like a substantial cognitive load cost. If you do care, getting granular control over mem/cpu in Go may be more frustrating than in Rust.
Maybe there is a third option. I don’t use it for memory management or performance, but mainly because I want things to be correct. And the way Rust forces me to think about error handling and option leads me personally in the right direction. And yes, that also means I’m not as fast in prototyping as I am in other languages, like Dart.
“Well, you could answer that Go is what you know, so that’s what you used to solve your problem, but that’s probably not going to be a satisfactory answer”
Why is that an unsatisfactory answer? When I have a problem, I reach for familiar tools and solve it. If that’s not the right answer because someone is questioning my “cred” maybe the problem isn’t actually technical…
Absolutely agreed; it’s a big reason I don’t agree with the RIIR sentiment, despite being a Rust booster in most other areas of the discourse. Working with the tools you know and that your colleagues know is almost always the right choice, for the simple reason that code needs to be read much more often than written.
Even pre-1.0, my (subjective) assessment was that Go’s traction was larger than OCaml’s. Certainly that was the case post-1.0. Was that not also your judgment?
I agree with parts of the article, but heres one thing it overstates: it says the first way of using C# await is almost never correct, and implies by contract that one’s first stab at using goroutines usually is correct. That’s not true. There are a lot of surprises around goroutines and channels. They’re nice tools for the job, but they don’t just make it super simple.
I think it would be interesting to explore which is easier to iterate on. It seems true that the first stab at any modality is usually wrong (with some wiggle room) but I bet theres more leverage on iteration.
I’m working on a new thing and I’m pushing to standardize 100% on Go (except for frontend). Is it perfect? No. Does it do 90% of what most services need, in a relatively clear and easy-to-learn way that is minimally prone to error? Yes.
In the past, we did a lot in Python – for testing, for some backend services, etc. But it turns out to be just as hard to write production high-quality Python code as Go code.
With new modules and generics, I have fewer and fewer complaints.
“Because its more important to me to build things of value than learn a new language” (Unless that thing of value is more language knowledge)
“Because I can”
…
The list goes on. They’re all appropriate responses that you don’t need to feel bad about. And they all go in any direction, e.g. “Why Rust and not Go?”, “Why Python and not Ruby?”, etc.
Go’s simplicity often just means that it’s lacking type hinting / systems that have been in modern languages for years. The “interface” is a hack, often times the programmer has to just throw their hands up an make an empty interface and infer the type later. Even though Go claims to have good concurrency primitives, I’ve had to track down multiple bugs that lead to undefined behavior when dealing with more complex concurrent structures.
I’ve never programmed in Rust, but I’ve used plenty of higher level languages that have proper type support (i.e. Erlang / Julia) AND good concurrency support via the former.
when dealing with more complex concurrent structures.
I’d presume race conditions.
I love go, but it’s approach to concurrency is “lets do things the old way, but make it less unpleasant”.
“The old way” is terrible; it takes forever to converge on correctness. The race detector is cute, but it won’t find obscure races, or races that only happen on an OS / CPU design you don’t personally test it on.
Do you know if there are any fuzzing libraries for scheduling? Something where I can feed in a program and run it 1000 times and the scheduler will try a bunch of edge case schedules?
Sorry, I was responding specifically in the context of Go. The race detector there will randomize the Go runtime’s scheduler decisions (which goroutine to schedule, which thread it will run on, etc.). I don’t know about similar options for processes and the decisions the kernel scheduler makes.
“Performance” is a many-headed beast (eg erlang has a great latency story). However, it’s true that there are problems with tight throughput requirements & highly-interrelated data that’s not amenable to partitioning.
However, Rust (well, safe rust) combines “manual mutexes” with compile-time static analysis which guarantees that you got it right via the ownership model.
Personally, I find the rust ownership model unpleasantly restrictive (though I hear it’s able to infer more and more these days). However, if I were going to write something that used manual synchronization, I’d reach for rust before anything else, because “borrow checker says no” is a much easier problem to solve than “user reports segfault every few weeks”.
I’m curious if you’ve had this experience with some Go code you were writing? Especially of the form “I deployed this to prod and discovered the issue in some nasty/hard to debug way”
My first job out of uni was fixing race conditions in a distributed database engine (which was a horrible multithreaded thing full of manual synchronization, AKA “the old way”). This gave me a pretty good eye for what sort of locking patterns were likely to be erroneous.
When I worked on a go team, I spotted (& verified the existence of) a couple of race conditions a year, mostly during code review, mostly based on “that looks like the bug I fixed all those years ago…”.
If I needed that kind of in-process concurrency, I’d be much happier with the way it’s done in rust, or the way it’s done in erlang.
Go’s type system does not have any notion of transferable ownership. When you start a go routine, the closure captures (by reference) everything in scope. All of these things are now shared between two concurrency contexts. In Go, all data races are undefined behaviour and so if you accidentally write to both then you are in UB land. This can trivially break memory safety because slices are a value type and writing to a slice is not atomic, so a store racing a load can result in the base and bounds getting out of sync. Now, somewhere later in your program, you get memory corruption. At this point, you may as well just write C++, because at least there’s mature tooling for debugging this kind of problem in C++. In contrast, the Rust equivalent of this would require either the Send or Sync trait to be able to either copy or move the reference to another concurrent context.
Go’s type system does not have any notion of transferable ownership
Transferable ownership is hugely beneficial with regards to safety, but it’s no panacea. Specifically, it prevents by construction a large class of high-performance design patterns.
“why did you write a small tool in a language designed for enterprise coding” is still a valid question, if we’re not accepting “It’s the language I know” as an answer.
But a lot of that sounds like an attempt to solve management/culture issues with technical solutions, and that’s rarely going to go well.
I’m talking about:
junior devs don’t get training
scope shifts over time
entire teams come and go over the lifetime of a project
Every language has its zealots but I have run into more Rust zealots lately. That makes sense, of course, since Rust’s trajectory and growth is amazing.
That being said, it is frustrating when I get asked (or scoffed at) for writing something for fun in a non-Rust language. It’s weird, and it’s happened more than once.
Regardless of your favorite programming language, if someone is excited to tell you about something they worked on…just be happy for them.
I write Ruby and Rust these days and I’m happy for you.
You should take up R instead. It will save you time when telling people about your favorite programming language. Also, I’m happy for you.
Completely off topic, but I feel like theres a pressure on tech forums to be happy for one another. But often times I do not feel happy for my fellow commenters, but quite angry at them.
or the dominant emotion, pity
For your contrarianism I feel contrarily at you, but I still respect you as a colleague.
Totally agree. I had an experience recently where a younger guy I know started taking a course online in python and databases because he really wants/needs to find a better job. And he called me worried because he talked to some in-law of his who was telling him he should be learning X, Y, Z languages and tools, so my buddy thought he just wasted money and time on these courses. I was able to calm him down, and have been spending time showing him linux command-line stuff to help him figure out his own environment issues (always the hardest part when you are starting, right?! even now!) which made him feel way more competent than he had before. So, definitely just be happy for people, and be encouraging when they are learning new things and encourage to not give up.
I went to a Rust meetup a few times and had this a lot. I’m much more a product builder than a programmer so my mentality is often “get it in front of people asap” so I’m usually hopping between languages to use whatever is the right tool for the job. Meanwhile, those who scoffed at me would be discussing compiling complex Rust code to the frontend just to avoid React for the simplest tasks. It was an odd experience but fun nonetheless, I’m happy for them!
I’ve gotten a few “why not Rust???” when mentioning I’m writing a game in C… It’s frustrating.
Why is it frustrating? Was it asked in a rude way or are you finding it frustrating regardless of the way it is asked?
My guess is that “the number of times you are asked” is a significant contributor. For the person asking the question, that’s a new info why not Rust for this particular project. For the person answering this same question for the tenth time there’s no information sharing, just tiring repetition.
Regardless of frequency it’s also just…kinda rude.
“I’m making broccoli cheese soup tonight.”
“Why not barley vegetable? It is so much healthier, after all.”
It’s vaguely presumptuous and condescending, implying that the person doesn’t know what’s good for them or that they don’t know their own tastes and skills.
Hm, not sure it’s rude in and of itself: I think it’s generally ok to offer people information you consider helpful, even if not explicitly asked about that. This helps to avoid the situation where you don’t know that there’s something you don’t know.
Or, more precisely, this depends on interlocutor of course: some people in some context loathe receiving unsolicited advice, some like it sometimes. But if you don’t know for sure (this is not a repeated interaction), you need to choose something, even if that’s asking to ask.
So, in general, I think politely asked “why not X?” is a great way for at least one participant to learn something.
Where this gets tricky is 1:N interaction, where you have a bunch of folks independently asking the same stupid question politely!
In general, I’ve had success approaching this by (EG) “Oh cool! I often make a barley-vegetable soup which I really like; let me know if you want to trade recipes sometime”.
I think the important differences are:
“Oh that’s nice I actually do this other thing” often still comes off as pushy, and engineer types do it A LOT. A more socially well adjusted (hypothetical) person would probably say “oh cool I’ve only had the bad canned type of broccoli-cheese, how do you blend in the cheese?” (something that demonstrates an interest in what they’re doing). Then after they have shared a bit more maybe say “I’ve been into barley-vegetable, yours seems delicious but I’m scared of the calories” even then it can seem pushy and is better to err on the side of not sharing your stuff because people always overestimate the value to the other person of their shit. ESPECIALLY engineers lol.
In the case of someone doing a project in C in 2023, it’s safe to assume they are aware of the purported benefits of Rust. “Oh cool what made you decide to do it in C” would be a pleasant way to ask for information if they are actually curious.
I wonder how much of what you call “socially well adjusted” is specific to American culture. In my central European country everyone seem to be much more direct and open to criticism 🤷
Yeah I wish I had more of this kind of discussion. I’m actually quite interested in Rust, I just kind of want to learn stuff from bottom-up. Seeing the benefits of Rust, Zig etc. will be easier when I have dabbled with C and C++ at first.
I’ve heard people generally don’t like either of these kind of responses, because they’re taking the conversation (A is eating delicious soup) and making it about yourself. The “correct” answer is “Oh, cool.” maybe followed up by “What made you go with broccoli cheese?”. People love the opportunity to talk about the things they enjoy, and they love the opportunity to ask about the things others enjoy.
[Comment removed by author]
[Comment removed by author]
Correct. It just keeps happening!
Sometimes it’s rude, but when I say I am doing this as a learning project, I get the whole “But Rust is much safer you should learn that now since it’s hot” spiel. Maybe I read into it too much.
Sorry, I didn’t mean to make you feel this way! I didn’t want to question how it makes you feel, I was just wondering if there is something specific that you are able to pin point (it’s fine if there is no such thing). People tell me I’m often perceived as too direct and you could call me rust fanboy so I was hoping to learn something here.
It’s things like how enjoyable is writing, building, and hacking with a language, that I care more for, than themes like scalability, enterprise use, or performance that the original post describes.
go doc | grep
is a fucking experience. Some time ago I was editing a Go program that needed to access the file system in a general manner. I know that package os can provide anfs.FS
view for the directory name I have at hand, but I don’t remember the name of the function in package os that does this. So I switch to a terminal and issue:Then I use
os.DirFS
in the program.The Go toolchain is closer to feeling like a Unix thing. Each build error from the
go
command produces exactly one line of output. None of thego
tool commands write colors in their output. But I’m glad thatcargo --color never
exists, and I aliascargo
this way. :)And on the hackability factor, Go programs tend to write themselves with just the standard library alone. For example, when I wrote a small static website generator binary some years ago, the program naturally hacked itself with only standard library imports and a single external markdown package. I wonder if it would have been as fun hacking it with Rust - vetting external packages and what not.
+1 re: stdlib. The ability to solve many non-trivial problems in Go (from web applications to low-level networking) with 0 dependencies is its killer feature.
I don’t know how and where you use Go, but if dig bellow the surface and see how most companies use it… oh boy… are you in to a gigantic unpleasant surprise.
Right now the system is full steam ahead in the direction of Java-esque corporatey BS. Don’t believe me? Head up to github, check a random go project and check the dependencies and the amount of code.
Almost all go codebases I have seen recently already do things like never, under any circumstances, referencing to a function directly. All calls go via an interface. The very same fundamentalism deprived of reasonable critical thinking that plagues the java world is already well establish among the Go community.
The meme is out that “Go is a better java”, I have seen that written in this very website a couple of times already, last week alone. People say this with a serious tone and don’t realize that they are steaming ahead full throttle towards the iceberg. They think it is a good thing.
Gather 100 Go developers and ask them to write a simple http service. You’ll be lucky if you find one that doesn’t immediately import some framework, router, whatever to handle the requests, rather than just using the stdlib. Same goes for things like reading environment vars, simple string interpolation, outgoing requests, etc.
I remind you that java (and other languages) didn’t lack such functionality in its standard distribution. To this day, after touching probably thousands of java HTTP services, I am yet to find one where the author simply used the httpserver included in Java SE distribution.
It’s naive to believe that go will be (is?) different in this regard.
I’m sorry that this is your general experience. I have also seen it in the wild. But I don’t agree that it characterizes “almost all Go codebases” in general. It is not difficult to write bad Go code like this, but it is also not inevitable that all Go code will end up like this, either.
[Comment removed by author]
As a Go programmer, I wish such drivel was at the top of the article, rather than in the conclusion. It would have saved me the time I wasted reading it.
Huh? From where I stand, Go does 100% look like a better(both in the sense “they solve similar problems” and in “actually better” sense) Java, up to repeating generics trajectory. It is similar to Java because
It directly improves upon Java by:
go
commands.C# also followed “better Java” trajectory, but it did so by adding stuff, not by removing stuff. I don’t know about C# enough to confidentially say that Go is better, but it surely does look like a defensible position.
And Rust is most definitely not a better Java, as the domains are wildly different.
I feel people think of Go as a better Java / C#, but it isn’t. And that false belief results in a lot of Go projects that should never have been Go projects, and suffer for it.
It’s fun and easy to meme on Java, but it succeeds as an application language and runtime because it’s actually good for building applications.
I definitely do maintain such a belief! I’d be very curious to hear specific arguments why Java would be a better language, and some specific examples of go programs which should have been Java programs. Of the top of my head:
It absolutely was the best “general purpose” language in 2000-2015. I think it is not that today.
This is why asking the question is rude. Somebody wants to show everybody an hilariously shaped rock they found gardening, and neckbeards in the room start arguing about what colour the handle of your shovel should be painted, and how blue is obviously better for digging hard ground while green is far superior for moving large volumes of leaf litter.
I don’t understand this comment (like, literally don’t understand relation between metaphor and the current conversation thread). Given upvotes, it probably is useful for me to understand, could you elaborate a bit?
It seems to me that the core implication (which may be exaggerated for effect) is that programming languages are as interchangeable as colors of paint, and practical differences between them exist only in people’s heads. (I don’t think I agree with that implication.)
Not at all. The core implication is that a) it’s ok to allow an entire conversation to run its course without interrupting everybody in the room to tell them why your favourite programming language is the best, and b) the arguments themselves are meaningless shouting about orthogonal points, and usually only matters of taste or opinion dressed up and presented as if they were proven facts.
Oh, and I think it’s meant in reference to this other thread. (I hadn’t realized the comment was outside that thread.)
Somebody writes a post about how they’re sick of nerds hijacking every single discussion to shout past each other about their current favourite programming language, and the comments in here instantly became exactly that; Nerds ignoring the topic and blasting the room with “facts” they made up justifying why their current favourite programming language is the best.
Thanks :-)
The Java ecosystem is closer to Django / Rails than anything in the Go ecosystem. If you want to make a new web app there are tons of tried and true Java web frameworks. The Go ecosystem is more a collection of libraries.
I also think Java the language is more expressive for building complex business logic abstractions. I’ve worked on a Go project where adding workflows required updating no less than 5 different boilerplate files. Could a different design have been better? Absolutely. But like all business logic it grew organically, and typical Go code doesn’t grow and adapt to new complexity without a lot of refactoring.
For more “systems-y” things, I would definitely prefer Go. For example, a scalable websocket server for an application otherwise written in Python. I’m also a big fan of Caddy. And I think Go makes an excellent language for command line tools. On the spectrum of C to Python, Go falls closer to C and Java falls closer to Python.
Thanks, this is an update for me: it does seem believable that, if you are building a rich web app with heaps of business logic (which is a common case), than you might get more mileage of JVM libraries and expressiveness(my understanding is that’s stuff like annotation processors, reflection, and other things which could make Java feel like Python, not the the nuts and bolts of more OO design)
Go has plugin support in the standard library: https://pkg.go.dev/plugin.
better not to talk about “plugin”. I don’t know that anyone uses it in prod
Thanks, didn’t realize Go has that! Though, it’s a thin wrapper over dlopen, and unix only, so probably not a good idea to try to build an Emacs out of that.
This package was always experimental and is (AFAIK) abandoned, useful for PoC code but nothing more.
The whole hashicorp ecosystem disagrees vehemently with you.
On the other hand, the hashicorp ecosystem is the only example of plugin architecture in go I can think of. But then, I don’t know a lot of go stuff.
From a quick look, it looks like hashicorp plugins are separate processes using IPC for communication. For this bullet, I had “shared memory plugins” in mind. It’s an open question whether shared memory plugins are a good idea at all. For example, in VS Code plugins communicate with the editor’s core via IPC. At the same time, plugins can communicate amongst themselves via shared memory, as all plugins are in the same process. Fuchsia is heavily IPC based, and that does seem like a good “from first principles” approach. And WASM with interface types confuses the picture even more, as it really blends IPC vs shared memory boundary.
I think it’s new enough that there are no mature applications with a WASM plugin architecture, but to me it’s such an obvious fit that I can’t wait to try it.
It’s not only new, it’s not really sufficiently there yet. You really need something like wasm components to get into the standard to describe meaningful plug-in APIs. Without this, plug-ins reduce to C ABI, which sucks.
In other words, a plug-in is a box with a computational device which does something, plus a bunch of toggles on top of the box as an interface. Today, WASM is great for describing the contents of the box, but it barely has anything on the interface side.
The original article also had emphasis on that word (italic). Maybe that was supposed to mean something like that it’s a newer language that competes in the same space as Java / C#, while Rust is a better C++ instead. I’m the post author btw.
Having worked professionally with both Rust and Go, I don’t agree that Rust has “worse overall productivity” than Go; indeed, I’ve spent a huge amount of time tracking down strange concurrency issues, poorly documented interfaces, and tooling issues with Go, and that hasn’t been an issue for me with Rust. That said, I worked with Go in the context of Docker internals, so that might be the root of the problem rather than the language itself.
100% yes, the more I use Go the more I discover it’s just a syntactic wrapper around C. Trying to maintain a project with transient dependencies is a nightmare (I’m looking at you /x/stdlib). It’s type system is so rudimentary that I struggle to parse json in a fashion that doesn’t have me writing functions like
doThingInt
anddoThingDouble
. And don’t even TRY to write any statistical / numerical modelling software with it, since it’s type system can’t even do basic conversions like Int -> Int64 automatically / intuitively.Those conversions are performance-sensitive; doing them automatically means unpredictable memory layout changes.
Personally, that kind of performance tweaking is totally irrelevant to the software I write, but if I were writing numerical modelling software I’d definitely want to know which integer type I were working with very early on.
I’ll provide a more concrete example. Let’s say I’m trying to model ocean flows, I’ll use a grid system where temperature / inflow / outflow are variables within the grid. The data type is extremely important, but once it’s defined in a structure, type conversion should be automatic. For different parameters, we’ll want to precisely control the floating point precision / size etc, but when doing numerical computations, we shouldn’t care what type the values are! We should be able to write a function that automatically performs the conversions while computing AND it should be able to perform the last conversion to the output variable that we’ve defined a float/int type for.
Edit: Floating point issues removed for brevity :)
Right - I had assumed that for numerical computations you would want to get the data into matching types ahead of time, instead of doing the conversion inside the loop. However, I can see that would sometimes involve an unacceptable memory use overhead.
I think this is a matter of what you’re working on. If the performance differential (in memory or CPU use) between Go and Rust is not that important, then bothering with lifetimes/the borrow checker seems like a substantial cognitive load cost. If you do care, getting granular control over mem/cpu in Go may be more frustrating than in Rust.
Maybe there is a third option. I don’t use it for memory management or performance, but mainly because I want things to be correct. And the way Rust forces me to think about error handling and
option
leads me personally in the right direction. And yes, that also means I’m not as fast in prototyping as I am in other languages, like Dart.upvoted, and if only because of this sentence
“Well, you could answer that Go is what you know, so that’s what you used to solve your problem, but that’s probably not going to be a satisfactory answer”
Why is that an unsatisfactory answer? When I have a problem, I reach for familiar tools and solve it. If that’s not the right answer because someone is questioning my “cred” maybe the problem isn’t actually technical…
Absolutely agreed; it’s a big reason I don’t agree with the RIIR sentiment, despite being a Rust booster in most other areas of the discourse. Working with the tools you know and that your colleagues know is almost always the right choice, for the simple reason that code needs to be read much more often than written.
I feel like “why Go and not OCaml” would be a more interesting article premise.
probably won instantly by Gos traction and ecosystem
There was a point when Go had a much smaller ecosystem than OCaml. Why did programmers choose it at that time?
When was that?
When Go was created.
Even pre-1.0, my (subjective) assessment was that Go’s traction was larger than OCaml’s. Certainly that was the case post-1.0. Was that not also your judgment?
My point is that every language has zero users and zero libraries when it’s created.
Those are merely useful, not interesting!
I agree with parts of the article, but heres one thing it overstates: it says the first way of using C# await is almost never correct, and implies by contract that one’s first stab at using goroutines usually is correct. That’s not true. There are a lot of surprises around goroutines and channels. They’re nice tools for the job, but they don’t just make it super simple.
I think it would be interesting to explore which is easier to iterate on. It seems true that the first stab at any modality is usually wrong (with some wiggle room) but I bet theres more leverage on iteration.
I’m working on a new thing and I’m pushing to standardize 100% on Go (except for frontend). Is it perfect? No. Does it do 90% of what most services need, in a relatively clear and easy-to-learn way that is minimally prone to error? Yes.
In the past, we did a lot in Python – for testing, for some backend services, etc. But it turns out to be just as hard to write production high-quality Python code as Go code.
With new modules and generics, I have fewer and fewer complaints.
“Because I like it”
“Because I get more done in it”
“Because its more important to me to build things of value than learn a new language” (Unless that thing of value is more language knowledge)
“Because I can” …
The list goes on. They’re all appropriate responses that you don’t need to feel bad about. And they all go in any direction, e.g. “Why Rust and not Go?”, “Why Python and not Ruby?”, etc.
Go’s simplicity often just means that it’s lacking type hinting / systems that have been in modern languages for years. The “interface” is a hack, often times the programmer has to just throw their hands up an make an empty interface and infer the type later. Even though Go claims to have good concurrency primitives, I’ve had to track down multiple bugs that lead to undefined behavior when dealing with more complex concurrent structures.
I’ve never programmed in Rust, but I’ve used plenty of higher level languages that have proper type support (i.e. Erlang / Julia) AND good concurrency support via the former.
What undefined behavior?
Given
I’d presume race conditions.
I love go, but it’s approach to concurrency is “lets do things the old way, but make it less unpleasant”.
“The old way” is terrible; it takes forever to converge on correctness. The race detector is cute, but it won’t find obscure races, or races that only happen on an OS / CPU design you don’t personally test it on.
Do you know if there are any fuzzing libraries for scheduling? Something where I can feed in a program and run it 1000 times and the scheduler will try a bunch of edge case schedules?
The race detector randomizes the scheduler decisions.
That’s just for threads, though, right? What about processes?
Sorry, I was responding specifically in the context of Go. The race detector there will randomize the Go runtime’s scheduler decisions (which goroutine to schedule, which thread it will run on, etc.). I don’t know about similar options for processes and the decisions the kernel scheduler makes.
Is it not the case that “the old way” is necessary for reasonable performance?
“Performance” is a many-headed beast (eg erlang has a great latency story). However, it’s true that there are problems with tight throughput requirements & highly-interrelated data that’s not amenable to partitioning.
However, Rust (well, safe rust) combines “manual mutexes” with compile-time static analysis which guarantees that you got it right via the ownership model.
Personally, I find the rust ownership model unpleasantly restrictive (though I hear it’s able to infer more and more these days). However, if I were going to write something that used manual synchronization, I’d reach for rust before anything else, because “borrow checker says no” is a much easier problem to solve than “user reports segfault every few weeks”.
I guess I just don’t have enough experience with code that can “segfault every few weeks” to speak to this point 😔
I really, really wish I didn’t either 😅
I’m curious if you’ve had this experience with some Go code you were writing? Especially of the form “I deployed this to prod and discovered the issue in some nasty/hard to debug way”
My first job out of uni was fixing race conditions in a distributed database engine (which was a horrible multithreaded thing full of manual synchronization, AKA “the old way”). This gave me a pretty good eye for what sort of locking patterns were likely to be erroneous.
When I worked on a go team, I spotted (& verified the existence of) a couple of race conditions a year, mostly during code review, mostly based on “that looks like the bug I fixed all those years ago…”.
If I needed that kind of in-process concurrency, I’d be much happier with the way it’s done in rust, or the way it’s done in erlang.
Go’s type system does not have any notion of transferable ownership. When you start a go routine, the closure captures (by reference) everything in scope. All of these things are now shared between two concurrency contexts. In Go, all data races are undefined behaviour and so if you accidentally write to both then you are in UB land. This can trivially break memory safety because slices are a value type and writing to a slice is not atomic, so a store racing a load can result in the base and bounds getting out of sync. Now, somewhere later in your program, you get memory corruption. At this point, you may as well just write C++, because at least there’s mature tooling for debugging this kind of problem in C++. In contrast, the Rust equivalent of this would require either the Send or Sync trait to be able to either copy or move the reference to another concurrent context.
Are you saying that built in race detector is not mature?
Transferable ownership is hugely beneficial with regards to safety, but it’s no panacea. Specifically, it prevents by construction a large class of high-performance design patterns.
“why did you write a small tool in a language designed for enterprise coding” is still a valid question, if we’re not accepting “It’s the language I know” as an answer.
But a lot of that sounds like an attempt to solve management/culture issues with technical solutions, and that’s rarely going to go well.
I’m talking about: