In my experience, Go appears simple as long as you’re solving simple tasks. Once you need to model fairly complex semantics, whose structure and detail depend on your business domain, Go code turns illegible, with lots of hackily lumped together bloated nonsensical channels and for-selects stemming from async IO and multi-threading forced into the goroutines syntax, with hard to formulate structured concurrency. And then you bolt on your validation logic on top of it, with several Go idiosyncrasies, and then you need to explain the distinct consumer-side interfaces, underwhelming generics and myriads of conventions based rules. One could claim that’s not how one writes “good” Go code. But that’s what depends on the complexity and dynamism of your domain logic and the folks you work with. After Java, production grade Go for larger projects is terrible. Null pointer method receivers, interface reification. And now the experiment with the functorial ranges, increasingly with more and more genetics, all without any syntactic sugar… hard to read, hard to explain.
Oh and the GC is rather inefficient compared to most JDK distributions. Compared to modern Java or Kotlin, for my project requirements, I don’t want to do as much Go as I used to. JDK Mission Control and Flight Recorder alone with great IDEs and profilers are golden. And you pay in RAM for JDK apps.
As for Rust, it just requires more upfront cost, but makes you consider potential failures at every step. Debugging production code isn’t fun, fuzz tests aren’t enough to assure quality to the extent Rust’s type system can. I trust rustc much more than I trust myself or other devs. If the code passes rustc, and the type semantics are correct, I’m 3/4 certain the code is correct, that’s a very high watermark, albeit at the cost of the upfront effort to satisfy the type constraints. Much more efficient in terms of dev time than debugging Python or Go at runtime.
Anyway, one can easily run many Python packages on GraalVM Truffle for direct interfacing with Java, Ruby, R, and via PyO3 with Rust, for which you have the native Polars data frame library, which all cover a good chunk of Python programs. Rust also teaches one to do proper ownership accounting for the variable bindings, great for C++ devs and for heap escape analysis by inspection for Go devs alike. Lots of Go bugs result from lack of understanding of ownership and implicit reference binding as opposed to copy and move semantics. Some of that was now “improved” with Go 1.22 for closures in loops.