Alright. I write programs as a hobby. I work as a sysadmin, so I'm not a "full blown developer". It's mostly a hobby. Sure, I write lots of code at work, but it's a different experience than a dev has. So, I might not have a "standard" perspective on things here. These are the tools that I currently use: - Shell: For simple stuff. Combining a bunch of pre-existing programs. - Python: For more complex stuff where performance doesn't matter. - C: When performance or minimalism matters. (I was using Shell for *much more* in the past, but that's a different story.) I'm happy-ish with the first two, but C is a problem. It is arrogant to think that I can "master" C. There is so much undefined behaviour. Maybe other people can keep track of that in their head, but I can't. My C programs are usually as simple as possible and I read them a million times to make "sure" I didn't mess up. External tools like valgrind can help, but they don't solve the problem entirely. So, for a while now, I've been looking for alternatives. Rust and Go are the obvious choices. At least that's what people tell you. "Go is a modern C!" "Rust is a modern C++!" Rust is incredibly hard to learn (which, again, is a story for another day), but at least it is fast. It's a bit slower than C in my experience, which is to be expected, but it's more or less similar-ish. Rust has other issues like a very small standard library (*yet* another story!) and, ugh, it's so hard. So, how about Go then? If it's a "modern C", isn't that what I want? The thing is: Those classifications only apply to *the language*. Specifically, Go has garbage collection at runtime -- and that is pretty costly. So costly, in fact, that I wonder if I can make use of Go at all. Here's an example: - https://uninformativ.de/git/buffyboxes - https://uninformativ.de/git/go-buffyboxes It's a program that scans my ~/Mail for maildir directories with unseen mails in them. The first one is implemented in Rust, the second one in Go. The Go version was super easy to write. Go is so much easier to use than Rust, it's a delight. This is, indeed, like comparing C and C++. But let's have a look at performance (this is "perf stat ..." on Linux). First, here's Rust: Performance counter stats for 'rust-buffyboxes': 134.81 msec task-clock:u # 0.994 CPUs utilized 0 context-switches:u # 0.000 /sec 0 cpu-migrations:u # 0.000 /sec 106 page-faults:u # 786.297 /sec 181,268,933 cycles:u # 1.345 GHz 349,395,875 stalled-cycles-frontend:u # 192.75% frontend cycles idle 413,494,813 instructions:u # 2.28 insn per cycle # 0.84 stalled cycles per insn 84,339,686 branches:u # 625.623 M/sec 264,641 branch-misses:u # 0.31% of all branches 0.135689355 seconds time elapsed 0.039639000 seconds user 0.095743000 seconds sys Now Go: Performance counter stats for 'go-buffyboxes': 325.31 msec task-clock:u # 1.081 CPUs utilized 0 context-switches:u # 0.000 /sec 0 cpu-migrations:u # 0.000 /sec 3,226 page-faults:u # 9.917 K/sec 576,142,657 cycles:u # 1.771 GHz 537,322,281 stalled-cycles-frontend:u # 93.26% frontend cycles idle 1,059,866,178 instructions:u # 1.84 insn per cycle # 0.51 stalled cycles per insn 246,705,344 branches:u # 758.371 M/sec 2,083,503 branch-misses:u # 0.84% of all branches 0.300841964 seconds time elapsed 0.242355000 seconds user 0.094261000 seconds sys Wallclock runtime is 0.135s in Rust vs. 0.3s in Go. The number of instructions issued and hence the required number of CPU cycles is more than twice as high in Go. More branches. "Seconds spent in user space" goes up by a factor of 6. Plot twist, here's my original implementation of the same program in Python: Performance counter stats for 'python-buffyboxes': 230.30 msec task-clock:u # 0.998 CPUs utilized 0 context-switches:u # 0.000 /sec 0 cpu-migrations:u # 0.000 /sec 3,056 page-faults:u # 13.270 K/sec 530,611,901 cycles:u # 2.304 GHz 469,803,499 stalled-cycles-frontend:u # 88.54% frontend cycles idle 1,145,782,086 instructions:u # 2.16 insn per cycle # 0.41 stalled cycles per insn 249,244,466 branches:u # 1.082 G/sec 1,328,806 branch-misses:u # 0.53% of all branches 0.230792465 seconds time elapsed 0.140332000 seconds user 0.090246000 seconds sys It's in the same range as Go regarding instructions, but overall actually a little faster. Long story short: You can't have both things. You can't have a super simple high-level language with garbage collection at runtime and, at the same time, blazingly fast performance. I've been really angry with Rust due to its complexity, but that complexity exists for a reason. Yes, this is just one example. I have a feeling, though, that it's more or less representative, because Go's GC won't go away. Go certainly has its advantages. For me and my use cases, I doubt that I will be able to replace C with Go, though. I might be able to replace *Python* with Go. Go is a compiled language and the compiler can catch lots of mistakes in advance -- this is really annoying about scripting languages. Then again, Go creates huge binaries and I usually don't need the static linking, so ... meh. We'll see. But C? That's probably going to be Rust territory. (For me. Not in general.)