“Sustainability with Rust” post misleading about Go
twitter.com
twitter.com
Once I realized that its sweet spot is large-ish services that you want closer to the metal than Java but for which would be silly to go as low level as C++, Go fans started making a whole lot more sense to me.
I'm still not a Go fan personally—my taste in programming languages slants toward the more expressive end of the scale (C#, Rust, Scala). But at least I'm no longer confused by trying to make Go make sense where it doesn't.
It's a very good tool for its kinds of jobs. Other languages also have their points of unreasonable effectiveness, too.
Source: https://www.karimarttila.fi/languages/2018/11/13/go-good-pro...
This proves my thoughts that these claims are not valid.
This is also misleading. You are implying that GraalVM isn't relatively new and that Java native compilation isn't rare. I've yet to see anyone deploy service or CLI tools that are natively compiled in Java. That statement is about as true as saying you can compile Python natively. Just because its possible doesn't mean the language is actually suited for it.
Nice joke. Go software runs much snappier and faster than any Java GUI or CLI tool.
Java CLI tools compiled with GraalVM start up just as fast.
Python is actually compiled. When you run a file, it firstly compiles the cod into a bytecode, which is saved in ‘.pyc’ files. These are afterwards interpreted by the interpreter.
Now what is the advantage again? The Java can compile to JVM bytecode and be better performing than golang, all while having superior introspection and monitoring and debugging abilities. It seems to be a win-win. Only some ideological "closer to the metal" philosophy doesn't apply, so what?
If startup times are truly an issue, look at Quarkus[1] and Micronaut[2], and a lot of other frameworks that use GraalVM to compile to native binaries.
Go and Java make different tradeoffs. If you're unreasonably effective in Java and its tradeoffs work for you, that's completely fine. If you have good tools that make Java more like Go when you need Go-like characteristics, that's cool too.
Additionally, JIT caches are now part of OpenJDK, OpenJ9 and ART.
Go has pretty good introspection with pprof it's enough, you don't need something heavy like JFR. For debugging delv is ok, not on part with C++/ Java but it's fine, you can even do remote debugging now.
The thing is with Java and to some extend C#, yes they have different runtimes, VM, AOT / JIT etc .. different thing to compile to native binary: none of them are really mature atm and moreover it's even more complexity for the programmer, now he has to choose which runtime or compiler he's going to use, it's crazyness. Same story with the GC Java offers, too much choice, too much burden on the programmer. For app A you're going to use this GC + runtime, app B is going to be different, it really mess up the dev / ops process.
Real world programs disagree. And for any large program, the JVM is a superior optimizer than golang's compiler.
> As for the tooling that Java has those it's because the language and runtime or so complicated with many layer of abstraction that you need those.
I heavily disagree. I deployed services both in golang and Java/JVM languages, and simplicity of the language or runtime has nothing to do with the JVM having superior observability and monitoring.
> you don't need something heavy like JFR
I wonder why my employer is spending millions of dollars on dev salaries to build something not even remotely comparable to the JFR for their golang programs then.
Secondly, the JFR is extremely light weight, and gives you < 1% overhead at runtime. Show me another ecosystem that gives you this out of the box.
> Same story with the GC Java offers, too much choice, too much burden on the programmer.
It doesn't really. The newer GCs have very few knobs to turn. Compare to when you hit GC issues in golang, the solution is to rewrite code in nonidiomatic ways as we saw with Discord. The first approach is superior.
Sure, Java has much more need of dev tooling than Go. But the available dev tooling on Java being enough better than the Go tooling to make the language + tooling have a better DX can still (if that is one's experience) be a valid advantage, for anyone not working in an artificially constrained environment where they are forced to code with Notepad and no tooling.
It's kind of true... but with so many caveats it's almost a lie.
* it compiles so slowly even basic applications can take 10 minutes to compile. What the hell are they doing all that time?!? Go can compile to multiple OSs architectures on the same machine and it takes a couple of seconds even for large code bases!!
* you can't use most of the Java ecosystem as reflection is endemic... good luck manually declaring everywhere reflection will be used.
* the loss of the JIT hurts peak Java performance a lot. Java runs as fast as Go *once the JVM is warmed up*! Native-image peak performance tends to be some way behind.
Not saying GraalVM native-image is useless, it can be used if you have the energy and knowledge to get past the issues... but as someone who konws Go as well as Java, if I am going to the trouble of going native, I'd just use Go without a doubt.
ExcelsiorJET, J/Rockit, Websphere Real Time, PTC or Aicas aren't GraalVM.
Also if you don't want to lose JIT, while having the advantage of AOT compilation, JIT caches are a thing, nowadays available for free on OpenJDK, OpenJ9 and Android (even though it isn't proper Java).
I never understood Go. I was sold as the C killer and failed, now it's basically an alternative to Erlang/Elixir, but suddenly people started running around and saying "Noooo, it was never meant to be a systems programming language like C!!!"
Secondly, Java can be tuned, but its default mode of operation is to take up the most amount of memory it can in order to give you higher performance (thoughput and latency). `-Xmx` is a thing.
Finally, now with GraalVM going mainstream, and frameworks like Quarkus[1] and Micronaut[2], you are able to compile Java programs to native binaries and also have lower memory usage if that's truly a restriction.
Some people really don't want to tune VMs. No one is saying Java doesn't have a place too. Jeez, dude.
I like Java (used to _love_ it about 26 years ago... been a decade since I've written any) but Go is actually a breath of fresh air for typical cross-platform systems programs.
Also GraalVM is not 3rd party but also not OpenJDK. It is Oracle product and where many perf related features are only in enterprise version. Also it does not support most of JFR JVM monitoring.
I am perfectly fine people/companies paying for performance. But it is not like Native image is trivially applied to any existing Java project. Either one has to use limited frameworks that you keep listing or spend endless hours in generating native json files to cover reflection cases.
Ironically Sun invented Java because lots of people bitched against TCL's non GPL license. Ironically, TCL/TK since 8.4 ran much snappier and faster than any Java turd even in 2004. Even simple Java 2D indie games looked laggish against AMSN, which was a "biggie" TCL/TK MSN clone back in he day.
But I think Java comes from something rather older, designed for interactive television, way back before anyone outside CERN had really seen a web browser. In that way the JavaStation is philosophically much closer to the roots of Java than the Java applet.
Java is planned to gain an equivalent of this ability through primitive classes (JEP 401). As as i know, we expect to get a preview of this feature in Java 19, which should be released in September this year.
1. It compiles statically down to machine code; there's no JIT or bytecode interpretation execution path.
2. It offers straightforward control over the memory layout of your data structures (it doesn't offer straightforward control over the lifecycle of your memory, which is the next step towards the metal that Rust takes).
The advantage is that casual Go code is generally going to be more efficient both in execution and memory usage than a casual program in a higher-level language. You can make Java do almost anything; the important thing is what languages make easy, not what they make possible.
Memory layout is a valid point though. C# with its value types gives you a lot more control than Java does, but is still often grouped with Java, and I think rightly so. Go's pointers go quite a bit further, but then on the other hand, it also has high-level data structures like map as language primitives, which makes it less close to the metal as far as I'm concerned.
I'd personally group all GC'd languages in roughly the same class here, but I think it's reasonable to disagree or make finer distinctions.
I'm also not saying JIT vs. AOT is an irrelevant distinction in practice -- like everyone else, I like my programs to start fast. But in the end, you have machine code in RAM either way; the level of abstraction is, all else being equal, the same.
The precise details of how that machine code gets there change nothing about how the program is written. The emphasis people put on those details aren't always warranted. I don't think they matter at all for typical backend code, which is one of Go's main niches.
Edit: The earlier version of this response was maybe unnecessarily pointed, I expanded and toned it down a bit.
Also, decoupling of the runtime from the language is huge, and lets us have nice languages like Clojure and Scala on the JVM.
It's funny -- I actually just watched this video yesterday: https://www.youtube.com/watch?v=5kj5ApnhPAE
Okay, but it's a pretty reasonable interpretation, right?
"Simpler X, in contrast to Y" makes it sound like Y is a less simple X.
Go is not suitable for the kernels etc kind of systems programming, but it's highly suited to the API services kind.
Ditto with plan9/9front and the original Unix.
Now, QT/C++ is the new Motif/C, but not just for Unix, but for doing cross plataform tools everywhere.
I think the future will be shipping static Go binaries with GIO everywhere.
INB4 "disk space", today's compilers and linkers should strip away all cruft from linking by removing any useless library function.
I would like to add a GUI to a little (very little) mysql database updater tool I have in customer hands. GIO is interesting and impressive but I am not as optimistic as you that it will develop into anything big.
Yep. I struggled and then I realised I could efficiently convert a complex shell pipe with a set of frustrating-to-the-customer prerequisites into a single, easily compiled cross-platform binary, and that it was really like Modula-2 and Occam to do it.
That's the point I became a fan.
It's a very reasonable objective. I just happen to not be in the target audience.
With Go you are going to be writing a ton of very explicit for loops. The error handling also feels a bit weird.
To me it seemed like a regression not to have map, filter, apply, reduce. I wrote my C for loops in the 90s!
To me something like Scala feels more Python like.
Both can handle similar data munging and ML tasks. Interfacing with DBs(relational or otherwise) again I'd take Python or Scala over Go just because of convenience factor.
I suppose it is also question of libraries. I've grown too comfortable with the vastness that is in pip or maven. By comparison Go library landscape is relatively bare.
Then again those native binaries of Go are so appealing...
I have a little C experience (in the distant past -- PVM, lex/yacc, Xview). I also have a little Occam and Modula-2 experience. Lots of Java and from then on a bunch of less comparable interpreted languages.
Go feels idiomatically more like a Modula-2, Wirth-language expression of Occam to me than C.
I like an awful lot of it.
> The error handling also feels a bit weird.
I think it is deliberately weird; it is designed to make you consider error handling right up front, which is where the "system language" thing comes in. No cascade of exceptions.
* control over memory use and layout (in contrast java is a pig over memory usage)
* some kind of easy way to use multicore (goroutines with channels or something else)
* speed, good cpu utilization
* garbage collected for ease of use
How many mainstream languages actually fit these criteria when Go came out? None? And how many fit these criteria now?
I'd argue C# was there (or very very close). C# has had value types (and fine-grained memory layout) since the beginning, reified generics since .NET 2.0, and also (lesser-known fact) raw pointer support (inside `unsafe {}` blocks much like Rust's approach). This was all motivated by interoperability with native code (mostly Win32 or other C-like ABIs & COM).
In 2009, the concurrency paradigm in .NET was more like promises and not quite as slick as goroutines. async/await wasn't born until 2012.
It wasn't on your list, but I'll toss in another point that works in favor of your original claim. ;) The main C# project also wasn't TRULY multi-platform until 2014.
Then what about those sweet web frameworks for Java and Python? Go makes it easy to write web services without relying on 3rd party frameworks but it does involve a learning curve which is not steep, There are some great examples[1] and thanks to readability one can learn what's happening even if they're just starting with Go(From other languages).
That said, I haven't had good experience with Go on low-memory environments even at 1GB, Perhaps it requires more thorough optimization in the code reg memory usage(which defeats the aforementioned productivity) and naturally a non-GC systems language like Rust might be better suited here.
Then I recently started experimenting with Go-WASM and found that the initialization less-intuitive(requires runtime wrapper), Compiled size is humongous and Go's concurrency prowess unavailable[2]. Another area where Rust seems to be a better fit but hoping that Go becomes better here, I don't want to use 4-5 different programming languages to build a single web application.
There are other things that I would consider questionable as well; Haskell might be capable of beating Go if you really, really work at it, but what you might call "casually written Haskell" isn't going to perform anywhere near as well as casually written Go. And the degree of knowledge required to write high-performance Haskell is much higher than high-performance Go. You have to have more than just knowledge of Haskell as a language, you have to have a fairly deep understanding of exactly how GHC compiles and optimizes it. This can be scaled to the Shootout, but scaling it to real code is very difficult.
Still, I think the general point is well-taken. I'm less worried about the environmental impact of my code because my code runs at scales where even if my code was running for "free" from an environmental impact, it wouldn't save much of anything. But having used Go for a while, I'd have a really difficult time moving back to a language that is literally dozens of times slower, and, yes, consumes dozens of times more energy as a result. Part of the reason why my code runs at such small scales is that it's reasonably efficient code. I have a couple of services that I consider trivial, but if I were writing them in Python, would be non-trivial. Still not exactly monsters that would be eating clusters of machines at a time, but there's still a lot of practical operational difference between "This occasionally spikes to 25% of a CPU for a minute at high loads" and "This occasionally spikes to consuming 10 full CPUs for a minute at high load", and that's a fairly reasonable multiplier. I have several services that would have deltas like that.
[1]: A previous comment of mine: https://news.ycombinator.com/item?id=25625362
a) Go was really sold as a "systems programming language" early on. Maybe some adopters never really questioned what that means or if that's true?
b) Go produces actual executables, with what in Java would be the JIT executed ahead of time and any runtime bits statically linked in. That is completely orthogonal to abstraction level, of course. But maybe the fact the there's no externally visible runtime makes people group the language into a bucket with C?
The other thing is that by not having a VM, a lot of usual Go performance optimizations end up being "closer to the metal." E.g. optimizing struct alignment is a fairly pedestrian Go performance optimization. On the other hand, if you rely on guarantees of how the Hotspot JVM (as opposed to other JVMs) lays things out in memory, that's considered pretty deep hacking in Java.
Having access to pointers is a superficiality. Java is getting value types, and C# already has them.
Valhalla's value types aren't the same thing as pointer access (or lack thererof). The first thing is that this is baked into the definition site rather than being something that can be decided at a call site. The second thing is that value types entail way more than just pointer access (as befits a higher-level approach). In particular it gets rid of the built-in ability of Java objects to act as locks, gets rid of mutation, etc. It's a much larger change than just referencing and dereferencing.
More to the point, the notion of pointers referencing and dereferencing is a lower level concept than reference vs value types. The former is usually used to implement the latter.
If the end result is the same, then the claim is really meaningless.
I'm just pointing out the parroting that goes on in the golang community.
One being used to implement the other doesn't mean the end result is the same (you cannot use value types and reference types to implement pointers because of the call-site vs definition-site distinction). As I mentioned, value types are far more than pointer dereferencing.
You absolutely _can_ do pointer arithmetic—it just requires using the "unsafe" package for obvious reasons (https://go.dev/play/p/Batmgb1KJcg).
Hell, there's even a helper function (https://pkg.go.dev/unsafe#Add) for addition!
This is not true even for C.
Secondly, it seems that its common in the golang community to parrot things that not only are incorrect, but provably wrong.
> Secondly, it seems that its common in the golang community to parrot things that not only are incorrect, but provably wrong.
Whatever your intentions may have been, this is just language war flame bait, and it's against the rules.
And it is good thing to have. But if one desperately comment same thing multiple times, I feel reasonable to ask why the hell they weren't added in all these years?
Value types have proven their worth, especially with the gap between CPUs and memory bandwidth, so they're being added.
However, given that Go also brings its green threading into the picture, I'd argue that it's actually higher level than either Java or .NET in that one respect.
RE threading, green threads though are coming (back!) to Java as well with Loom, but I consider that more of a library/runtime thing than a language thing (as is the case with Loom). A hypothetical Go library could expose system threads and have an API for manipulating them, it just wouldn't be first-class in the same way goroutines are. But this is similar to how having async-await in C# doesn't negate the fact that you can use direct system threads from the standard library.
And then there's stuff like Span, which these days lets you do something similar to Go slices:
Span<byte> p = stackalloc byte[n];
(note that this is also safe code!)As for green threads, I think in case of Go they can't really be considered a library thing due to language integration of goroutines and channels. It's also not quite the same as C# async/await, because the latter isn't really a threading system so much so as syntactic sugar for CPS; it's entirely possible to have a single thread running the event loop.
> As for green threads, I think in case of Go they can't really be considered a library thing due to language integration of goroutines and channels. It's also not quite the same as C# async/await, because the latter isn't really a threading system so much so as syntactic sugar for CPS; it's entirely possible to have a single thread running the event loop.
Again this isn't really a language thing anymore though, it's more of a runtime/operational detail. You could implement goroutines with CPS if you wanted and async-await could be implemented by green threads. Off the top of my head I can't think of anything in the API of goroutines or async-await that would allow you to distinguish whether they're being implemented by CPS or green threads in the background.
This is not entirely true. Refs on lower level are pointers - when it compiles down to CIL, the resulting types are rendered as T&, and referred to as managed pointer, as opposed to T* the unmanaged pointer.
And you can use T& as an independent type - it's not a high-level annotation on a pointer, but a fundamental CIL concept. The limitations that you describe are all about not allowing a T& that points to a local on the stack escape the function where said local is defined (whereas Go silently lifts the local to heap in this case). Hence why you can't have T&&, and why a struct that has a T& field must itself be declared as a "ref struct" (and have the same restrictions applied to it).
On C# level, refs were originally a function argument modifier, as you say, solely about pass-by-value vs pass-by-reference, implemented under the hood as managed pointers. But since then, we've got the ability to return refs, declare local ref variables and fields (in ref structs), and reassign them. So at this point they're effectively types, with some restrictions on use similar to Rust borrowing.
> You could implement goroutines with CPS if you wanted and async-await could be implemented by green threads. Off the top of my head I can't think of anything in the API of goroutines or async-await that would allow you to distinguish whether they're being implemented by CPS or green threads in the background.
To implement goroutines with CPS, you'd need to rewrite every call into CPS-style. This is technically not observable, yes; but perf would be atrocious in practice, so it's not going to happen.
Async/await, though, is literally defined in terms of callbacks in the language spec; threads are completely orthogonal to all that. The dispatch loop can decide to run continuations however it sees fit, including threads - but at this point we're talking about callback-oriented code that is already desugared from "await". And running that on top of green threads is possible but pointless, since the semantics of await requires that async context switching happens only at awaits; but the ability to pre-empt at any arbitrary point is precisely the advantage of green threads.
> This is not entirely true. Refs on lower level are pointers - when it compiles down to CIL, the resulting types are rendered as T&, and referred to as managed pointer, as opposed to T* the unmanaged pointer.
Right and I don't think anybody would disagree that CIL is not a low-level language. But plenty of high-level language features compile down to just pointers. This doesn't mean that the language offers pointers though! In that world I could say that Python offers pointers (or indeed any high-level language that compiles to LLVM and has a feature that just corresponds to a pointer).
> But since then, we've got the ability to return refs, declare local ref variables and fields (in ref structs), and reassign them. So at this point they're effectively types, with some restrictions on use similar to Rust borrowing.
No they're not. You still have the `Ref<...>` vs `ref` distinction (which is why ref fields aren't a thing), you still can't have nested `ref`s, and you still can't switch between "ref"ing and "unref"ing something, i.e. ref-ing is infectious.
Likewise the fact that refs have some linear characteristics doesn't make them pointers (in the same way that Linear Haskell doesn't mean that Haskell now has pointers). The reason why we say that Rust has pointers (apart from the raw pointers), is that they don't have any of these issues!
> To implement goroutines with CPS, you'd need to rewrite every call into CPS-style. This is technically not observable, yes; but perf would be atrocious in practice, so it's not going to happen.
Yes there are sound reasons for why Go has chosen certain implementation decisions, but those are not part of the language semantics, which is ultimately what determines whether a language feels high-level or low-level.
> And running that on top of green threads is possible but pointless, since the semantics of await requires that async context switching happens only at awaits; but the ability to pre-empt at any arbitrary point is precisely the advantage of green threads.
No current industrial green thread implementation I know of offers pre-emption, they are AFAIK all cooperative. IIRC Go's implementation yields on channel sends and GHC's implentation yields on memory allocation. I think Loom is heading for pre-emption, but it hasn't been finalized yet. But that's not really the point. My point is special syntax for a concurrency feature that is not backed by system threads does not preclude a language from using system threads, which is the fundamental relationship between Go's goroutines and any FFI that would expose system threads. Or put another way, being unable to use system threads are not a fundamental part of the semantics of Go, while being unable to use pointers are a fundamental part of the semantics of safe C#.
By default Go will be faster because of the difference of value types between the two languages, Go is also easier to optimize since the language is simpler and has less abstraction.
Compared to Java?
> it feels more like a JITed language than compiled
Our experience is that typical Java programs will consume a lot more memory, and spend a lot more time doing GC and other unproductive work than typical Go programs.
Go provides a lot more tools that give more bang for the buck when optimizing. For example, everything in Java is a reference to some other distant blob of memory. This does not work well on todays cpus compared to being able to colocate things.
We run more stuff on the same machines than we could with Java.
Team wise, Go is easier to learn, and scales better with teams. We don't have trouble with team members struggling with certain team member's code / abstractions like we did with Java or C++. Switching between the two, there is a lot more cognitive overhead in Java land. It's understandable that this may be boring to some, but it is a great feature of the language.
That’s definitely way in the other direction - Java’s GCs run circles around Go’s. (Part of) the reason for Java’s bigger memory consumption is that it is very lazy when it comes to GC. Go runs it all the time for lower latency (at the price of much lower throughput) while Java allows for control it.
I've worked on two large Python projects which would have been trivial in Go. In Python, we had to scale them out to multiple machines and ultimately an Apache Spark cluster (after trying all of the multiprocessing / numpy / Cython / etc gimmicks). The naive Go implementation was a dozen times faster and dramatically more maintainable. Unfortunately our principal engineer couldn't be reasoned with--he understood that in general switching languages isn't a panacea, but he didn't understand the specific problems we were encountering due to Python nor how dramatically Go could improve on them. (He was an inexperienced frontend engineer who was rapidly promoted because he would make shiny demos and hand them off to other teams to fix / extend / operate, and predictably that startup failed).
It's so interesting that people usually have this complaint about Rust, but here it was a rewrite to Go that was seen as problematic for some reason. Sometimes you just can't win.
Ultimately, the more interesting story is how this person was able to "fail upward" so effectively. Basically, nontechnical management don't understand backend software, so the person who makes it "real" for them via user interface demos is the person they associate with manifesting the working feature, even if user interface demos are superficial. Moreover, management only interacted with features by way of his demos, so they didn't understand how much work was required to productionize his demos. The features he debuted would be handed to another team to productionize and support. That team would appear to move slowly as they mired through his tech debt while this principle engineer was moving onto yet another demo, giving the appearance that he was our prolific 10x engineer.
In his defense, if you work in Python and JavaScript a lot, you get pretty jaded on tools and techniques that promise to be silver bullets and the idea that you could have a language that is orders of magnitude faster than Python, easy to learn, better package management and other tooling, and at least as maintainable just wasn't believable. Frankly, I think it would have been an easier sell if I lied and said that Go was only slightly better than Python.
any% (most languages call out to pre-written machine code, probably)
language% (anything possible using just that language)
idiomatic%, casual%, singlethreaded%, memory%, compile-run%?, safe%? etc.
Then at least we'd be able to argue about which categories a particular benchmark result would apply to, and separately argue about which categories matter in a particular domain.
Asking Rust programmers to set the rules for what counts as "idiomatic" Perl makes no sense, just like asking Cuphead players what counts as a "major" glitch in Java Minecraft or Portal 2.
But if there is no agreement you haven't really improved anything on the measuring front.
But now that we're arguing about specific categories there's a chance that benchmark comparisons could be useful. Right now the whole enterprise is a cluster; categories aren't perfect but they would be an improvement.
And I can believe a chart could tell me Bob the C++ programmer is better than Jim the C++ programmer at some task a community of C++ programmers agreed on.
But can it tell me that Bob the C++ programmer is better than Sarah the C programmer? Or more so, that C++ is better than C? No, the C++ and C communities are going to disagree about how to measure this.
Even a category like this is subject to debate, because of intrinsics. C++ compilers provide many "intrinsic" functions that map to machine code instructions, but these functions aren't defined in the standard and the names can vary between compilers. So are they part of the language? ¯\_(ツ)_/¯
> Second, the summary of the Discord post about switching from Go to Rust is incredibly misleading. The original Discord post showed a single graph plotting both a Go server and the equivalent Rust server. The Rust line had more predictable performance and avoided the latency spikes in the Go line, but the lines were roughly comparable. Instead, the AWS post displayed the Go graph next to a graph of the Rust server after a significant rewrite to use new data structures and more RAM, circling “ms” vs “µs” time scales. This is either a complete misunderstanding of the Discord post or blatant dishonesty.
The discord post [2] says this:
> After the service ran successfully for a few days, we decided it was time to re-raise the LRU cache capacity. In the Go version, as mentioned above, raising the cap of the LRU cache resulted in longer garbage collections. We no longer had to deal with garbage collection, so we figured we could raise the cap of the cache and get even better performance. We increased the memory capacity for the boxes, optimized the data structure to use even less memory (for fun), and increased the cache capacity to 8 million Read States.
He paraphrased "optimized the data structures to use even less memory (for fun)" as "a significant rewrite to use new data structures". The post doesn't say how involved the optimization was, but I got the impression it might have been pretty trivial to shave a few bytes off the most common allocation or some such.
And as for increasing the cache capacity, that's something Rust allowed them to do without problems but Go 1.10 didn't, so I think it's entirely fair to include it in the comparison. Maybe Go 1.18 would also have been able to do this, but unless Discord dusts off their old server to aid people choosing a language in 2022, we're not going to really know...
[1] and even made a similar argument here: https://news.ycombinator.com/item?id=30313943
[2] https://discord.com/blog/why-discord-is-switching-from-go-to...
But Discord also made other changes during the rewrite - changing from hash table to B-tree, data structure layout optimizations, reducing memory copying, etc. And the graphs are with those changes included. So the Discord graphs aren't a pure Rust vs Go comparison. I guess I can agree with rsc that it's "misleading" to cite the graphs during a Rust vs. Go comparison.
But the headings of the Discord post imply the latency change for average response time from milliseconds to microseconds is purely due to increasing the cache size, which was only possible due to eliminating the GC spikes. So it is fair to include that as an issue in a Go 1.10 vs Rust comparison. rsc's opinion seems to be that Go 1.18 will fix the latency issue - I disagree, but the only way to tell is to redo the measurements.
I've never seen a "we switched program $X from language $Y to language $Z" post that reproduced exactly the same program, and I don't think it's reasonable to expect that. This one doesn't seem to have done anything crazy like a completely different algorithm, SIMD intrinsics replacing naive code, etc. (The Game's programs actually are that different, so I don't like that study at all.)
The programs that use SIMD intrinsics are split-out:
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
I'd say Mathnerd314's comment is more correct than your nitpick. Reference counting is arguably a form of GC, but I think most people don't call it GC, and there's a reason the JVM and Go have much more sophisticated GCs. For starters, weak references are often insufficient as a mechanism to handle cycles. And really what Mathnerd314 was getting at is that Rust programs typically don't require a GC where Go ones always do. Pointing to a Rust GC library (and there are some) doesn't change that either; it misses the point.
Benchmarking software is so easy to do wrong. It's so easy to manipulate the results if you are so inclined, and even when you're trying to be evenhanded it's so easy to disadvantage one of the systems under test.
Different approaches are called for with different tools, and the classic error we see over and over again in benchmarking studies is the inappropriate application of idioms which suit one of the competitors across the others.
Benchmarks can be useful as long as you can actually understand the reasons for the performance differences to be able to evaluate the pro and cons of the benchmarked systems.
Stealing this.
As my friend James Kanze put it, "never trust a benchmark you didn't falsify yourself."
https://stackoverflow.com/questions/5326269/is-c-sharp-reall...
Look at the data tables published with that 2017 paper:
https://sites.google.com/view/energy-efficiency-languages/re...
There's an order of magnitude difference between the times of the selected C and C++ programs, for one thing — regex-redux. That is enough to explain all of the difference between the C and C++ time measurements shown in "Table 4. Normalized global results". (Seems like an outlier which could have been excluded.)
It's true that lots of valid C programs are also valid C++ programs but it is not true that those programs will mean the same thing when treated as C++, nor does it mean that all C++ compilers are automatically just as good at optimising all possible valid programs as C compilers, and so the code which you get out and then measure may be quite different.
With newer C versions, there is "restrict", which can lead to better optimizations. And VLAs to avoid some heap allocations (albeit at the cost of potential stack overflow).
In most cases, giving the compiler more information to work with makes the resulting code faster. C++ is generally slightly faster than equivalent C, even though the difference is usually very small.
I have to agree with the OP, C being 34% faster than C++ is simply an absurd result.
When you wrote "always flawed" perhaps you meant inevitably flawed ?
If not, then perhaps you could suggest a practical approach that would not exhibit that variation.
I think what would matter more is that C++ encourages different patterns which may be less efficient. So instead of using an on-stack buffer you may use a std::vector or instead of a data-structure tuned to your application you may just use std::unordered_map. Of course this can bite both ways, the provided data-structure is likely more performance optimized than what you will write (most of the time) so if it is a good match for your use-case you may get better performance from C++.
So I don't agree with "C++ can't be slower than C because that C is C++" because your average C++ isn't C. It is like saying "C can't be slower than asm because you can just embed the asm in your C program", it is true after infinite optimization time, but doesn't reflect how most of most C programs are written.
What are those?
Yes, but when you have a million of allocation, that still means two million instructions. malloc/free allows a much easier path to allocating a large chunk of memory in which you can play around with your million structs having only the overhead of accessing pointers (and, of course, thinking about where to point them).
libc++: https://github.com/llvm-mirror/libcxx/blob/master/src/new.cp... libstdc++: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-...
I think that you overlook how the model of new/delete encourages thinking about memory in individual chunks (of the size of your class) instead of a small number of large chunks that can be split into individual pieces as needed, but make the overhead of allocating/deallocating memory much, much lower.
Thanks to everyone that has contributed to these great languages! It's so nice to be able to simple things that are such an after-thought in other languages.
And Go and Rust are kind of appealing to totally opposite audiences who want the same thing: a fast binary.
Rust is a complex language with a lot of features, making it very difficult to master. Go is at the very other end of the spectrum: very simple language, very few features, really easy to learn and write. Some people like one, some people like the other: it's great that we have both, so both kinds of people can write fast software!!
Sure, Rust is faster, I don't think anyone will argue against that, but Go is still plenty fast and lightweight. So I am really glad my only choice are not only C++ and Rust, and that Go exists (as well as OCaml, Java, Kotlin, Dart, Haskell, D, Zig, they all have something going for them and people who get attracted to their ethos which just expands the number of people who can produce something using a language they can relate to!).
As a thought exercise, think what happens when a company engages a consultancy to make a Postgres backed Java or Python app running in AWS to replace a business process run previously in a overgrown Excel sheet and brain of a soon to be retired domain expert. Programmers are hired, (second) cars are bought, overseas vacations are had, what happens to co2 emissions? The choice between Java or Rust makes zero difference. The choice between Java and Excel might, but not because of runtime performance...
TLDR; almost always it's the development budget, not CPU use.
[1] You can see this number by country at https://ourworldindata.org/grapher/co2-intensity - about 0.1 - 0.6 kg/$ in industrualized places
I'd like to see tech industry emissions as a whole, just for running software. I have to imagine it's pretty minuscule in the grand scheme, but still interesting. What does a product like Instagram that has a huge user base and an application written (mostly?) in Python create in emissions and energy use?
I guess we'd also have to consider that the societal benefit of Instagram isn't as high as trucks that deliver food to grocery stores, so truck emissions may be seen as more necessary emissions.
It's a really deep rabbit hole. I find the concepts interested, though don't put too much thought into these issues I don't directly control.
Fair points about c++ and the overall deficits in the original study that doesn't use comparable programs across languages.
Edit: deleted java vs go comment, and replaced with the evidence at hand
https://sites.google.com/view/energy-efficiency-languages/re...
That outlier should explain the Typescript and Javascript difference in Table 4.
Today with `node-v17.6.0` those same programs don't show that magnitude of difference:
43.268 seconds Node JS
77.029 seconds tsc --strict --noEmitOnErrorhttps://greenlab.di.uminho.pt/wp-content/uploads/2017/10/sle...
Also, a suggestion for anyone who plans to do an update: please put a version number on the languages you use, and also note which specific versions of compilers, VMs, GCs, etc. were used.
https://haslab.github.io/SAFER/scp21.pdf
The rankings move a little bit, but the relative positions of Go and Rust don't.
The measuring framework and the complete set
of results are publicly available at
https://sites.google.com/view/energy-efficiency-languagesHe doesn’t seem to be aware that The Benchmarks Game has a moderator who requires the contest entries to be “idiomatic” for each language (details of what counts as idiomatic are of course contentious), so a C program that doesn’t use C++ features/idioms would not be accepted in the game.
You will probably come across people saying that the programs are not idiomatic ("enough"). So read the description and write your own idiomatic program, without programming tricks.
That's not the same as "requiring".
More like — if you aren't going to contribute idiomatic programs then don't complain when we don't show idiomatic programs.
Look at the data tables published with that 2017 paper:
https://sites.google.com/view/energy-efficiency-languages/re...
There's an order of magnitude difference between the times of the selected C and C++ programs, for one thing — regex-redux. That is enough to explain all of the difference between the C and C++ time measurements shown in "Table 4. Normalized global results". (Seems like an outlier which could have been excluded.)
Let's keep focused on AWS's poor greenwashing post. You could replace all the names of the technologies and it's still just as bad.
Communities are painted toxic all the time rightly or wrongly. To say community have feeling and are to treated as person does not seem to be appropriate.
Also calling people toxic who called some community toxic is akin to name calling and I dare say much more common in Rust circles.
In particular, the referenced AWS article is typically called out as a bad "study"
Maybe it was different a few years ago, but this is what I've seen since late 2019ish.
But, these are engineers at huge companies rewriting core system components for dubious reasons AND writing a deliberately misleading blog post about it.
Now more engineers with an agenda would cite these posts to their managers and get their rewrites.
I've had senior tech leadership explain that rewriting a big system into a new language is done because the real goal is to rewrite the old system that no one understands well enough and is gradually becoming scary to change; people are enthused for the shiny new language, and at the end the current organization understands this crucial system for a few more years.
People in companies write the blog posts because it helps get promotions ("visibility" "industry leadership").
This is definitely toxic work culture that needs to be fixed
Blog posts about them are often interesting simply because you get a new perspective on the tools you use frequently.
I'm talking about the types of things where a rewrite is done not for the purpose of learning, or because a specific set of issues is identified in the existing project, etc...but because "this language is better so I'm rewriting X in it"
I use the prevalence of "In X" posts as a sneaky measure of maturity of the given language. A language is "mature" when people start posting projects written in it on HN without highlighting the implementation language.
Rust & Go have both hit this, BTW. But only a couple of years ago, and you can still definitely see "In Rust/Go" posts with some regularity, so they haven't hit full maturity, where it wouldn't even cross the mind of a poster to mention Rust or Go as if it were a weird thing.
Where specifically did you encounter the toxicity?
It also seems like a large portion of the posts I see in embedded Rust are about generics and/or async. I have no interest in them. (Most of the embedded code I've seen using them can be written more plainly without). It also turns new people off the language due to the syntax clutter. So, they think "Rust is hard to write/messy". It's not the language - it's how parts of the community use it.
I also dislike the condescending attitude when C code comes up .
My opinion has always been: Be Confident Enough to Allow Others to Disagree. Some in the Rust community sometimes have a problem with this, but, my God, it's not as if the arguments against Rust are very good, or as if people don't love to troll Rust, like the parent to your comment. Most criticisms are embarrassingly flimsy.
Which is not to say that Rust doesn't have a number of downsides. And I'm writing this as a Rust enthusiast and (very minor) contributor to the Rust compiler.
Again, you should feel free to disagree. I guess what I'd say is that your experience has not been my experience. And I'll keep an eye out for you on the forums if you want to discuss Rust!
Not sure I convinced my self here. But yeah, something like that.
Although I'd disagree re: ease of development, because it's highly subjective and depends on your use, I would agree that it's wrong for some to characterize Rust as a floor wax and a dessert topping. It's really good for lots of things and less good/still growing for others.
Doesn't mean that we all share the same definition of "ease of development" or that your ease of development would similarly improve – it might very well suffer, depending on the specific pain points that you're up against.
So may I suggest rephrasing "ease of development tend to suffer" into "learning curve may be harsh"?
Incidentally, I don't think you'll find much hype in the core Rust community. This community has always been very honest about what Rust does well, what Rust doesn't do so well, and what Rust isn't good at at all. I found this refreshing because I never quite saw so much honesty in, well, any other community, I guess.
>Ease of development tend to suffer
to be true. I'm sure it is true in certain contexts, such as building certain types of data structures, or making GUIs, but for normal day-to-day development work it feels like I'm getting performance, security, and ease of development.
It's even worse when really senior figures in a community are also the source of the toxicity.
That said, I have not interacted with the Rust community and cannot make any comments in that regard. Language is pretty nice though.
I'll leave with this: I'm a super introvert who works from home, so I took my difficulties as possibly me needing to take a break.
I actually track Rust quite closely since there's a lot of neat and innovative work coming out of it, and I hope to join the community since it seems like many have had a lot more positive of an experience.
Where do you have these interactions? I'm frequently on the Rust forums and the few times I have seen something toxic brewing, someone has always stepped in to politely ask the fanbois (yes, they exist) to stop.
Reddit is so full of trolls that I've mostly stopped contributing, except in a few subreddits that are properly moderated.
Sorry you experienced that :/
For what it's worth, I don't think I've seen anybody from the core Rust community on /r/rust.
And to be very clear I’m not trying to say there is no overlap at all, but I don’t think you can call the Rust community toxic because of those people.
(I wonder if the people who treat Rust like a religion realize how harmful their behavior is. I want Rust to be seen as a reasonable option, but that’s difficult when you have the evangelists scaring people away)
That is certainly true, but for pretty much every community that I know, not limited to any programming language or technology.
Asking, because I use it daily :)
Additionally the author was reluctant, and in the final case outright refused, to accept fixes because they weren't interesting. For that last one, the fix was to replace the custom Cell (or RefCell, I forget) implementation with the one from the standard library. The big "dog pile" happened on that last one.
Still, I have to say, it's really weird to see (last few months) everything being rewritten into Rust, just for the sake of pride(?).
Haven't we learned anything from the NodeJS craze?
It's to prevent all memory safety bugs.
But there is a broad-based desire to rewrite libraries in Rust for a couple (IMHO valid) reasons. One is the API. While it is possible to have a nice and idiomatic Rust API to a C library, it will often be nicer to use when it is designed from the ground-up in Rust. Traits, interfaces, etc.
It is also often desirable to avoid C when cross-compiling (such as for embedded systems), as that is generally a hassle to set up and get working seamlessly.
Also, it’s not like rewriting such trivial tools as cat in rust would cause any conceivable amount of useless churn. They are not rewriting complete ecosystems.
I'm both on the Rust forums and on the Rust Matrix community and I haven't seen anything toxic at all. Everything I've seen that looked like it could become toxic was handled gently by someone with a bit of seniority.
Where did you ask?
I don't even get why Golang is considered a good language. The new goto primitive for concurrency, the half baked error handling, the fact that it's designers think that programmers are not smart enough and made a language that looks like Java and feels like Python (but hey, no class inheritance and the compile times are faaast) are just a few of the examples that don't make it good enough for me. I wonder how popular Golang would have been if it wasn't backed up by it's company.
What is this tendency for the software industry to try and make everything "easy"? Trying to oversimplify something that is inherently complex only adds accidental complexity.
But yeah it seems to be more of a competitor vs Python and Ruby than vs Rust.
Programming languages are there to make programmers productive. Otherwise we would all work in something like assembly (I've done that but try to stay away from it now).
Go attempts to do this by hiding the complexity. When people have to spend time dealing with language (or platform) complexity they have less time to spend on their business logic or the core thing they are trying to do. By making the complex simple for the majority of cases they enable people to be more productive in solving their specific issues.
I say all of this as someone who likes and has issues with both Go and Rust. And, every language I've ever used.
100% agree and I find I can produce more software value [1] per unit time/effort in Golang than anything else.
Is it the perfect language? No, there are certainly some warts, but no show-stoppers, and no more than other languages.
[1] "software value" being defined as software that meets its requirements, is easy to write and build initially, easy to maintain by others, and performant enough not to need extra-ordinary hardware.
Have you looked at the history of the creators of Go? If not, I would suggest looking into it. 2 of the 3 have an amazing history in terms of computing.
I think Go is notable for its scarcity. It has few features which interact well together. It is extremely light in abstraction, so understanding whats really going on takes far less work than basically any other language out there (maybe C is a competitor here, but C is hard to understand for other reasons). Simplicity is not novel, but it allows the programmer to forget about questions which have little relevance to the problem they're trying to solve (e.g whats the ideal type structure for this program) and focus on questions more like "how should this work".
It's a language that recognizes that cleaning your room by stuffing your clothes under the bed actually works in programming.
For golang it lists: Kubernetes, Docker, Github CLI, Hugo, Caddy, Drone, Ethereum, Syncthing, Terraform
For Rust it lists: Firefox, ripgrep, alacritty, deno, Habitat
While both lists are a bit cherry-picked and some not really true, it is certainly true that there are significant more (I'd guess 1 or 2 orders of magnitude) large projects using Golang.
What do you make of the fact? Certainly it's not programmers that want to be treated like they're not smart. Nor can you assume all the different team's engineering leaders made foolish decisions.
[1] https://thenewstack.io/rust-vs-go-why-theyre-better-together...
It has its use cases, it's basically a faster python, and it's good for writing command line apps since it compiles to native code. Other than that, it has nothing going for it especially compared to proper mature server side languages like Java and C#.
Even its compile times are not that impressive when programs get large, which is quite ironic.
I think Go's been a mature base for those kinds of projects long enough for them to have been written. Rust not only is newer but arguably is still pretty immature for network programming. For sync code, the library base isn't there and probably never will be (because most of the libraries are being written for tokio instead). For async code, there are still a bunch of well-known difficulties. [1] This is my biggest complaint about Rust. People are aware of it and working to improve it, so I hope to see a change over the next several years. It will never be as simple as Go, though. Go's green threads model has downsides but it is really pleasant and simple to program in.
[1] e.g. see https://carllerche.com/2021/06/17/six-ways-to-make-async-rus...
Golang hit 1.0 in 2012, Rust in 2015. That's a big factor.
Golang has been marketed heavily by Google, Rust doesn't have an equivalent sponsor. That's also a factor.
There's also a difference in focal domain, with Rust’s focus in places where evaluation and replacement cycles are naturally longer and that are deeper at the core of infrastructure.
Where is this marketing by google? Though I don't really understand the point of this line of questioning anyways.
Sounds like bullshit to me. Go did not even have proper Google branded website until last year.
This has translated to me as Go is for the weekdays and Rust is for the weekends. It's the equivalent to the 2000's Java for work and $LiterallyAnythingElse for personal stuff. It's a mindset that will age the same as well, I think.
But also, as far as adoption goes, things are changing. My work environment - which is Microsoft, so "big serious company" - is embracing Rust. There's production code written in it, and there's public support (see e.g. https://docs.microsoft.com/en-us/windows/dev-environment/rus...). So at this point I would say that Rust should be considered as having equivalent corporate backing and expected longevity.
Because it's a more 'get shit done' language (with a fast solid runtime, good stdlib and and fast compiles), it's not concerned with giving its users the means and ergonomics to produce the most beautiful programs, instead, a language for the majority of backend developers.
Unless you can elaborate more, this is pretty meaningless. I could make the same statement about Rust or literally any language and it would be just as substantial.
- people who code in Rust and hate it do so because they keep fighting the borrow checker;
- people who code in go and hate it do so because their code is buggy.
Now I code regularly in Rust, I enjoy it very much and I very much understand why some people dislike the borrow checker. I prefer Rust but I very much understand why some of these developers may prefer go (or Python, or TypeScript, etc.).
On the other hand, I don't code in go (sufficiently) to know whether go is as full of gotchas as developers who hate go make it look.
Maybe? I'm not convinced. If that was the case, I assume that I would have seen someone accusing Rust of shooting them in the foot.
Again, while Rust is my current favorite language, I make no claim that Rust is perfect by any metric.
> but it means the language isn't getting in the way also.
Probably? I don't practice go sufficiently to be a judge of that.
I know that the Rust community would definitely assume that the compiler (or at least clippy, the standard linter) should get in the way of anything that is obviously (or not-so-obviously) going to cause a bug sooner or later.
slice = append(slice, item)
Note that you must have the assignment for this to work correctly. But if you don't, it'll work correctly some of the time - whenever the backing array doesn't have to be reallocated.Now, yes, the linters do catch it. But it's hard for me to consider a language mandating such a pattern as well-designed.
That... doesn't feel me with much confidence in the language (or, well, its stdlib).
I'm sure that there is a tradeoff somewhere and that you gain something by allowing this in the library. But I'm strongly on the side of developers for whom reliability has a very high priority.
It would be a different story if append() always returned a new slice and never updated the original data - then, yes, having to assign is the price you pay for immutability and its benefits (as in e.g. F# or Clojure).
But this is not the case here - it's the way it is simply because Go designers didn't see it fit to have a proper abstraction for a dynamically resizable list, and punted it all down to slices (a lower-level abstraction) instead, which the users are then expected to apply in correct order.
My experience is that these people hate go because it's less expressive and more repetitive. See `if err != nil` as an example.
I wouldn't guess it's super fast, but the super-power of Go is you can use all those cores efficiently without getting dragged down and murdered by a lot of mutex/lock stuff, and it's efficient and fast with medium-high volumes (few K/second of messages per core, that sort of thing).
Both of those servers were left behind and maintained successfully by other people, so I don't think they are examples of a pile of tech debt. They are invisible network appliance type code, but customized to be super-precisely what was needed for specific network problems.
It is as fast to dev in as Python, lets me mix and match data structures and algorithms via problem-specific structs, and is able to use my hardware.
As you point out, its design philosophy is overly simplistic to the point of being almost naive. Complexity exists in the real world, there's no getting away from it, and if you keep your language deliberately "simple", you are pushing complexity to the user.
I wouldn't even say golang looks like Java. Java has evolved quite significantly in the past few years, adding many useful features that further aid in program modeling ability and expressiveness, like records and pattern matching. Not to mention it getting green threads that are going to be better implemented than golang's "goroutines" by virtue of having structured concurrency built in from the start.
The world is complex, it's just reality. The question becomes where do you want to push that complexity, on the user or on the language? golang decides the former, which is why you end up with messes like the kubernetes code base.
But there is a tradeoff. Every complex feature you add has a continuous cost applied to every reader, in that they have to become expert with the new feature. C++ is a great example of the failure mode here, and even Java has too many features such that using many of them will confuse most of my coworkers. Having few features means that the code is more accessible, and you can spend your cognitive load on the genuine complexity of your problem rather than fiddling with language knobs.
Obviously, it can be taken too far as with the case of C++, but golang is in the opposite extreme where a lot of code does very little and is not clear what it's doing.
> and you can spend your cognitive load on the genuine complexity of your problem
Which is what a good language lets you do. In golang, a lot of time and cognitive load is spent trying to express your ideas in very unsafe and verbose manners.
I think my problem is that this simply doesn't match my real life experience. In reality, the Java code (which is the majority at my company) is much more likely to overabstract and thus be very challenging to read than the go code I interact with. So, while your argument is compelling at a distance, it seems to me it must be flawed as it simply doesn't match my experience.
I would suggest the flaw might be something like number of lines of code not being equivalent to reading difficulty. Like, yes go code hasa more lines dedicated to if err != nil {}, but those lines are low density and easy to scan. Its a bit like reading light fiction as opposed to Dostoyevsky.
As for error handling, compare
var res = foo(bar(), baz());
and f, err := bar()
if err != nil {
return err
}
b, err := baz()
if err != nil {
return err
}
res, err := foo(f, b)
if err != nil {
return err
}
It's much harder to follow what's going on. Now add more complex code (e.g. code that does validation or something and you'll see how it blows up) var res = foo(items.map(i -> i.validate()), bar());
and validatedItems := make([]Item, len(items)
for i, item := range items {
var err error
validatedItems[i], err = item.Validate()
if err != nil {
return err
}
}
f, err := foo()
if err != nil {
return err
}
res, err := foo(validatedItems, f)
if err != nil {
return err
}
1 line maps to over 15 lines, I don't think that's readable.Whether that's true is beyond the point, which is that the majority of Java code out there is in fact overabstracted. That should serve as a hint that there may just be something about Java that does lead to overabstraction.
> As for error handling, compare [...] It's much harder to follow what's going on.
Making error-handling code paths happen implicitly in the background isn't a straight win. For example, you lose the ability to tell a fallible function from a non-fallible one, especially at the point of calling it.
Overall, I find that being able to see all code paths explicitly laid out is better, Go syntax aside. Keep in mind that these examples are tiny, and therefore it's trivial to see how exceptions would implicitly propagate. They become much harder to track as the size and complexity of a codebase grows.
> Now add more complex code (e.g. code that does validation or something and you'll see how it blows up) [...] 1 line maps to over 15 lines, I don't think that's readable.
I wouldn't say the amount of lines is a great indicator of readability, not unless the difference was in the ballpark of maybe 1 to 100.
---
I don't think this code sample is representative:
- what would happen to this pretty one-liner if I wanted to catch the exception raised in `i.validate()` and raise a different exception instead?
- you can create the .map() function in Go too
- the Go code size will not grow linearly as you add more .map()/.filter() calls
I feel like a good example of Java code would be much denser, leveraging several expressive language features that Go doesn't have.
I wonder why our points of view on this are so different. May I ask what your background is? Perhaps you come from more dynamic languages than I do?
Until a few years ago, I used to write C++ and JavaScript for a living and OCaml for fun.
The book "A Philosophy of Software Design" has been formative for me.
I understand how go can feel like fresh air after suffering from Enterprise-style Java.
In my experience, the largest sources of bugs and general misery in C++ code is that the language is way too complex, that both the language and its libraries are full of footguns, and that debugging these footguns requires heroism and burnouts.
In fact, that's almost the same complaint I have against JavaScript, except there is a strong veneer of "don't worry, it's simple". Which is unfortunately a lie, as can be confirmed by anyone who had to read the actual specs of the language.
All in all, I favor no-surprises-at-runtime because I have been bitten waaaaay to often by surprising behavior in production despite what felt like comprehensive unit and integration testing. That's in both C++, JS and Python. Hence me being really happy at Rust's guarantees.
As for Golang fanboyism which contrary to Rust is almost none existent, I mean on every HN / Reddit post you're going to get someone that will tell you about Rust, why would you use a language with GC, error handling, no sum types etc ... hinting that it's the best language, something that you never see with Go users.