People, in general have this weird desire to ask everyone to use what they do. I am on that team. I want the world to use Linux, ditch Windows and I am all for LibreOffice, against all MS monstrosity but I think that all such people forget that the middle-path is the best one. With time, I have gone a bit easy on things. I use macOS and Linux. I use Word for what works best in Word (which I still don’t know whatever that is, except drinking up all my RAM), I pay OneDrive because that is the cheapest reliable Cloud Storage I could get.

When I tell people that I work with Go, one of the first recommendations/questions I get are about Rust. Over at Techrail Discord I have been asked multiple times about my views about Rust. Do I know it? Do I plan on using it? Or why I don’t build/rebuild things in Rust. In this write-up I am trying to settle that debate about why I want to stay away from Rust.

Ruby

It is a running joke that I have with my spouse - that I love Ruby more than I love her. Because I can sing praises about that programming language like a teenager obsessing over how amazing his crush looks with that beautiful flowy dress and open hair in the wind. Sometimes till she starts drowsing right there. I love that language and whenever I show the powers Ruby bestows upon you to someone who hasn’t seen that thing, they more or less are mind-blown by it. Saying things like “I didn’t know that was even possible!“.

But honestly, Ruby is slow. Ruby is probably the slowest of all the programming languages I have worked with. But it does deliver on its “Programmer Happiness” motto. It has been around 8 years that I first worked with Ruby (Ruby on Rails). Up until then, I had worked with PHP and JS mostly. One day I was returning from office, walking down from the bus stop to my place and I suddenly realised “I like this life”. It was astonishing to me because I would sometimes just let those thoughts from the office run in my head about how to solve upcoming problems. And it was probably the first time when I fully realized what Ruby had done to me. I was no longer thinking in terms of “Ok… I would have to define a function that waits for the webhook and then I would sanitize the input, check if the item is in the DB, check the item’s state and then call one of the 6 functions depending on what the state is”. When I say that (quoted line), I mean my thought process would often include granular details about what kind of type checks would be there, kinds of variables to capture in an error message and so on.

That evening I realized that I was thinking in terms of “Once the webhook is there, I would fire the function to process the state”. I was not thinking about how to split strings, parse json, sanitizing input, querying DB. None of that. I felt happy. I was thinking at least two levels up. I was not caring about nitty-gritties. I was thinking in terms of what I need to achieve.

Before I encountered Ruby, I used to think and feel that it is the responsibility, and the job, of the programmer to do all those things - checking strings, parsing json, input sanitization, querying DB and more. Ruby, rather Ruby on Rails made me feel that it was my job to think in terms of “what to do to get the work done”, not to care about those super fine details.

But yes, Ruby is slow. I think it was around that time that I ran a test comparing Ruby and PHP with some basic number crunching and I found Ruby was about 9 times slow and I for once did not care. If you knew me, that would have been unusual for anyone to see me and think “this guy is going to be happy torturing the machine for doing basically the same thing 9 times slower”. But I ignored all that because Ruby made me happy.

Go (Golang)

I got introduced to go at work as well; and in a company right after I had worked with Ruby. It was astounding for me to really see a language that was this small. I mean, there are so many features missing that I can’t describe. Compared to Ruby, go sits in the opposite direction.

Go is more or less diametrically-opposite to Ruby

  • Go is compiled. Ruby is not.
  • Go is easy to download and setup. Ruby requires you to perform a ritual before you can get it working for your needs.
  • Go is fast. Back in the day when I started, people used to compare Go’s performance to C! Ruby is one of the slowest.
  • Go has no OOPs. Ruby is the closest I have found to implement OOP in its original sense as imagined by the creator of that term (Alan Kay).
  • Go is concurrent. Ruby will sweat and make you sweat if you ask it to handle more than 5 threads in parallel.
  • Go asks you to handle errors one at a time, has no exception handling. Ruby has one of the best.
  • Go doesn’t have one of those “the best framework for this language” kinda libraries. Ruby has Rails.
  • Go’s templating sucks. Ruby (at least Rails’) templates work like a charm.
  • Go will make you bang your computer if you wrote something detailed enough to handle “any data type”. Ruby does that by default for you.

I could go on and on into the details but you get the gist, I believe.

It was so different from Ruby and I had not loved learning anything more than Go. It was the first language that I learnt in a day. It was the third day when I was already writing services in Go, with middleware and all! This was a level of simplicity that I had not seen.

So why do I love go?

I have worked with Java, PHP, JS, Ruby and Go on the backend. Go is one of my most favorite ones. Right alongside Ruby. Why? Because Go is so well placed in its domain that it’s unbelievably good. Following is why I think it is so good.

  1. It is simple to learn and to work with. Like I said - took me 2 days. Ruby took me about 7-15 days to learn to get to that point.
  2. It is fast. Its programs have a speed that rivals C. It’s easily comparable to Java on speed.
  3. Like Ruby or Java, I don’t have to care about variable scoping, memory allocations and so on.
  4. But unlike Java, I don’t have to worry about stop-the-world GC pauses for most cases anyway.
  5. There is no OOP but the most useful features of OOP are there because of composition (via embedding), variables and functions can be hidden or exported from a package to another using capitalization of the first letter.
  6. Functions can return multiple values without having to declare a new struct type for everything.
  7. It is cross platform - not only for installation and execution but also allows cross-platform compilation (you could release a Windows binary for Intel architecture using your ARM64/Apple-Silicon macOS machine, or vice versa).
  8. It allows you to create a binary which needs no runtime to work. You can simply compile the program to get the binary and just transfer it to another machine and it will work. No installations needed (like Python, JVM, Node etc.).
  9. It allows you to think in parallel. I can (and have) written small systems where the web and workers are both included in the same program. I can make a single program expose 3 web servers handling requests from all fronts while sending emails in the background and processing tasks - all in a single program.
  10. It allows you to think from a “tasks” perspective, rather than a “threads” perspective. That simplifies a lot of stuff. You don’t worry about concurrency, thread synchronisation, mutexes, memory safety (except when dealing with Slices, mostly) etc. This model has been copied by a lot of old and new languages. Java, Kotlin, Rust - they all have some sort of copy of this paradigm now.
  11. Go demands that you know the hardware at least a little. That’s good for a programmer as he progresses. While in the beginning, it doesn’t force you, working with Go will make you learn about the hardware just enough that you would appreciate it. And yes, I count that gradual insistence towards deeper understanding feature.
  12. Go can be all safe but it allows me to go unsafe when I need it. There is a whole unsafe package that allows for greater control over things. I like that. Not just that, it also allows you to bind and link to a C program using a feature called “CGO”. Now, I haven’t used CGO in my own work but I have used some libs that do and that’s awesome.
  13. Did I mention that go programs are fast!?

Did you see how the list progressed from simple one-liners to deeper advantages? Go will do that to you - and it’s both good and bad. It of course has its own bad parts.

Poorly designed generics, risk of going OOM because GC didn’t run fast enough (honestly, I have only read about it, not encountered it myself), error handling that is verbose and forces you to think about everything you do, lack of enumerations, no OOPs and so on. But then there are hardly any languages that are perfect in that regard.

I think the part I love the most is how it handles concurrency without having to worry about OS threads. Before encountering go, I was afraid of writing concurrent (multi-threaded) code. Now, I can’t live without it.

Rust!

First things first - I think that is a weird name for a language. Maybe it was named as such to mean that this thing is “super close to metal” but still, it’s a weird name. And it has a weird logo as well. A Crab. But then, those are not imperative points. I mean Zig’s mascot makes me feel worse. So that’s subjective and non-imperative about a language.

Now, I don’t know Rust beyond the fact that things are immutable by default and functions are defined with fn. Yes, I can read a bit of Rust code because I was going through a course to learn this language. But I stopped midway to run away from it. And I’m gonna tell you why.

About 8 months back (from the time I am writing this), I was introduced to “Omakube”, a setup made for Ubuntu by DHH (creator of Ruby on Rails). Part of that setup is a terminal multiplexer called “Zellij”. I like Zellij and I prefer it over tmux for the same reason that people prefer nano over vim - the instructions and key bindings are shown right on the screen! I have and still use multiplexer apps for running long sessions; like, if I want to login to a machine remotely and run a long running command without having to worry about whether my SSH connection breaking would kill that program, I would use something like tmux or screen or zellij. Ok. So I liked Zellij.

I had (still have) a Raspberry Pi 4 which I sometimes used to program in by logging in remotely. I needed my programming sessions to stay there. So I needed a terminal multiplexer. Since I had fallen in love recently, I wanted to get Zellij working on this machine. I realized that there are no pre-built binaries available for zellij that can run on Raspberry Pi. So what do I do? I try to compile it myself. And sure, there are instructions available on their site to compile it myself.

I do a rustup, and a cargo build. It downloads the dependencies. And then? Then it starts compiling.

5 minutes in. Compiling dependencies.

10 minutes in. Dependencies being compiled.

15 minutes in. Yup, still compiling dependencies.

17 minutes in. Dependencies are done.

I took a small sigh of relief. Now it starts compiling the main program, Zellij itself.

5 minutes.

10 minutes.

15 minutes.

I am thinking “holy mother of god. what the hell has happened!? Should I leave it be or should I try to restart the process? Maybe something is broken?” I checked the CPU stats and rustc has heated up that little computer. All thanks to a large heatsink, the CPU is stable at around 60 degrees (which is fine for a RPi4, honestly). So I wait.

20 minutes.

25 minutes.

30 minutes.

I am now getting impatient. Checking CPU usage again, realising this was a mistake and questioning my life choices.

35 minutes.

37 minutes.

Done!

It took me close to an hour (50+ minutes) to compile a terminal multiplexer program written in Rust to compile. Yes, Raspberry Pi is not a beast of a machine but it is fairly powerful for most such tasks. I have seen a large Go program compile which was millions of lines too if you include the dependencies compile faster on a 2 core machine inside a docker container.

I did a test and ran the same compilation on various machines. It was much, much slower on all of them. On a MacBook Pro that took about 2 minutes to fresh-compile the million-lines go monolith program, Zellij took around 8-9 minutes. All for a terminal multiplexer.

That was the moment when I gave up. God forbid, if I start liking this language and build something useful and I need to build my project fresh, what am I looking at? I am looking at wasting my time upfront.

And what gains am I looking at?

Rust has speed. And memory safety

But so does go. Go programs are fast. They are memory safe for most parts. You have to do something awfully complicated and esoteric for a go program to start leaking memory without the garbage collector being able to collect that back.

If you think you want to reach down to metal and control everything, sure you can’t do that entirely, but it is quite possible to just stop doing anything that pressures the GC. For example, do not use slices, but arrays, use byte arrays instead of strings, do not return pointers from functions and so on and you will be able to do both the things that you get with Rust:

  • Extreme performance.
  • A difficult life.

Unless you are dealing with the unsafe package in go, it’s quite hard to go memory-unsafe as well.

Y I no Rust

So what are my gains with Rust, after dealing with the Borrow Checker and horrible compile times? I will summarise my hope in a small line and let you decide if it is worth learning Rust to replace go for most (at least mine anyway) usecases:

Comparing Rust to Go, I would waste maybe 4 months worth of time in learning and compilation to save 4 milliseconds.

Depending on your usecase, that might be worth it. For me, it does not sound like a value proposition.

And that is the reason I stay away from Metal exposed to moisture.

Summary

I love two languages that are conceptually diametrically opposite, both for their unique features and capabilities - Ruby and Go. But I do not have months to spend to gain milliseconds. Since Rust’s compile times are horribly high, it is a no-go (pun intended) for me.