Look, benchmarks are bullshit. They tell you how fast your code runs in a perfect world with synthetic workloads. They don't tell you how long you'll spend fighting the language to make it do what you want.
Rust: Fast Code, Slow Developer
Rust's borrow checker is like having a really smart, really pedantic coworker looking over your shoulder. Every time you try to do something slightly clever, it tells you why you're wrong. And it's usually right, which makes it worse.
The error messages are actually pretty good once you learn to read them:
error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
But "good" doesn't mean "obvious." I probably hit this error fifty times before I understood what the hell it was trying to tell me. Solution was usually to clone stuff or restructure the code completely.
Async Rust is where things get really fun. You want to share some state between tasks? Cool, now you need Arc<Mutex
Go: Boring is Beautiful (Until GC Strikes)
Go compiles stupid fast. Like, blink and you miss it fast. Writing Go feels like using a Honda Civic - not exciting, but reliable and gets you where you need to go.
Then you hit production and realize the garbage collector is a real thing that actually pauses your program. Our latency graphs looked like a heart monitor. Every few seconds, boom, 200ms spike because GC decided it was cleaning time.
You can tune it, set GOGC=50 or whatever, but honestly most of the time you just live with it. Or you write your code differently to generate less garbage. Which is annoying but doable.
The race detector is legitimately amazing though:
WARNING: DATA RACE
Read at 0x00c000102040 by goroutine 7:
main.handleRequest()
Just run your tests with -race
and it'll catch the concurrency bugs you didn't know you had. Found a map we forgot to lock. Would've been impossible to debug otherwise.
Zig: When C Doesn't Hate You (Much)
Zig feels like C if C wasn't trying to hurt you. The comptime stuff is actually pretty cool - you can run code during compilation which is wild if you think about it. But it's not even 1.0 yet, so expect things to break.
Had a segfault in production once:
fish: Job 1, './server' terminated by signal SIGSEGV (Address boundary error)
Turns out I messed up memory management. Shocking, I know. But at least Zig gave me a decent stack trace when I compiled with debug info. In C this would've been a nightmare to track down.
Compilation is usually fast, but sometimes the linker decides to take a coffee break. Seems to be a Linux thing - works fine on Mac. Cross-compilation is supposed to be great but I haven't had much luck with it.
Performance though? Really damn fast. No GC pauses, no borrow checker fights, just native code doing native code things. Used way less memory than the Go version too.