Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
“Sustainability with Rust” post misleading about Go (twitter.com/_rsc)
190 points by 0xedb on Feb 23, 2022 | hide | past | favorite | 332 comments


I struggled for years with a chronic misunderstanding of Go. I kept trying to compare it to the usual suspects in the "next systems language" crowd (D, Rust, Zig). That wasn't quite right.

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.


> 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.

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.


Rob Pike actually defined it exactly this way when it was introduced -- a simpler systems language, in contrast to Java.

It's funny -- I actually just watched this video yesterday: https://www.youtube.com/watch?v=5kj5ApnhPAE


Wasn't Go essentially Limbo from Bell Labs, like Rob Pike and others who moved to Google?

https://en.wikipedia.org/wiki/Limbo_(programming_language)


Go is like what C++ should been long ago, from the creators of C.

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 think the future will be shipping static Go binaries with GIO everywhere.

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.


Limbo + some touch of Oberon-2


Is Java really a systems language?


"systems language" and "systems programming" are terms whose interpretations vary widely across the industry. At one extreme, it means kernels, drivers, and embedded, the stuff where you need to precisely manipulate bytes with no infrastructure and minimum overhead. At the other extreme, it means any software whose users are other programs rather than people, so API services and so on. My first job was as a "systems engineer", writing database middleware (kind of) in Java.

Go is not suitable for the kernels etc kind of systems programming, but it's highly suited to the API services kind.


In fact, the terms are becoming more and more diluted. "Systems Engineering" is a good example; it actually used to be a well defined discipline (see EIA-632, IEEE 1220, ISO 26702, etc.) until some non-US universities started to use the term for disciplines like system administration or computer network engineering. Similar confusion e.g. exists on the question of whether C is a high- or low-level language. The funny thing is that Go is even used as a low-resource embedded system programming language, e.g. for the micro:bit.


Maybe it's a regional thing, but I don't know anyone who calls that "systems programming". I'd personally use the term "backend programming".


That's not what I said. Pike described golang as a systems language, and specifically contrasted it with Java. Perhaps you should watch the talk.


> That's not what I said.

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.


[flagged]


> a simpler systems language, in contrast to Java.

I read this as saying the contrast between go and java is that the former is simpler. The confusion maybe comes about because "simpler" is a comparative adjective (as opposed to, eg, "simple", which is a descriptive adjective), and because there are up to two qualities being contrasted -- simplicity and "being a systems language".

Take it or leave it but I personally was a bit confused by the wording.


> of course we don't think of Java as a systems programming language

It's not "of course" when we're in the middle of conversation about different definitions of "systems".

> You should watch the video if you want Rob Pike's reasoning as to why golang is a systems language

I might do that later, but either way you wrote an ambiguous comment and I don't know why you're lashing out so badly that people try to explain that. Such things happen.


I'm sorry you were confused. Buck up. It'll get better. I promise.


I think I agree with the point you are trying to make but I think the form in which you are making it is alienating people.


So another way of saying this is consider the space of languages where

* 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?


Go was definitely ahead of the curve on a few things.

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.


To be fair Java has made some strides in memory usage. GraalVM reduces it a lot and with Valhalla (and to some degree Loom) it should be more competitive on that front (and others) with other languages.


I'd like to add the recent addition of ZGC as a massive benefit the JVM. Pause times always measured in microseconds regardless of heap size. No other GC:ed language has that capability as far as I know.


As far as I am concerned, Go feels like an attempt to write a systems-oriented Python. Or perhaps a systems-oriented language targeting Python developers.

It's a very reasonable objective. I just happen to not be in the target audience.


Go felt to me more like improved C with GC - not surprising when Ken Thompson and Rob Pike are involved.

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...


The funny thing about Go is how many different things you can recognise in it.

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.


I think that's pretty much aligned with Google reasoning back then. Go was designed to make it easier to onboard their fresh grads + scientists hiring,which were more familiar python to do systems stuff.


Yes, there is a well-established PHP to Go pipeline among developers.


Here's where I've settled at; Any web services for which I used Java, Python before is now written in Go. The productivity(Easy to write/read) & Cost(Concurrency saves $ at scale) improvements makes it hard to not use Go here.

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.

[1] https://github.com/fragmenta

[2] https://news.ycombinator.com/item?id=30356020#30357078


Define "closer to the metal than Java". What exactly does that mean, and how exactly is that an advantage?


Two major things:

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.


When the actual machine code is generated is irrelevant to the level of abstraction. There have been JIT compilers for C, and ahead-of-time compilers for C# and Java, and that changes nothing about how close to the metal any of them are.

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.


By that logic, every language is equivalently close to the metal when it comes to execution, since they can all be JIT'd one way or another. It's a colorable argument but not one most practitioners would agree with.


No, I would agree that something with a smaller runtime might be considered closer to the metal, I was talking about level of abstraction, which I think is different. One example is that a typical C code that depends on a libc is farther from the machine than C code that runs without a libc. Both might use very similar styles.

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.


The one significant thing i can think of is that Go makes pointers explicit, and allows variables and parameters which are by-value. So you can control whether some struct contains a pointer to some other struct, or just embeds the fields directly. This gives you influence over memory use patterns, access time, and garbage collection that you don't have in Java.

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.


Go does not have bytecode or a VM, it compiles directly to a binary with a runtime.


Java (and C# I believe) can also compile to binaries. And golang has a runtime as well.

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.

[1] https://quarkus.io/ [2] https://micronaut.io/


I am mostly a Java guy, but when people claim that Java can also compile to binaries, I really don't like that.

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.


Also good luck if you need to cross-compile. The Go toolchain for that is seamless, Graal’s is non-existent (because it is fundamentally incompatible with the way native image compilation works).


If you are cross compiling while using Java, you really don't get Java.


If you are building and distributing executables using Graal native-image, you probably care about cross-compilation.


Which is an very good example of not understanding what Java is all about.


Can you please just say what you mean instead of posting vague, shallow dismissals?


WORA.


So you are a Java guy and are only aware of GraalVM, while ignoring all the AOT commercial offerings since 2000?

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've been working with Java since 2009 only... those things you mention have never been used anywhere I've seen since then (but yes, I heard of them as things of the distant past that no one used).


No one insulted Java. Why so defensive?

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.


I'm pointing out the inconsistencies in golang fans arguments which I've seen from the very beginning since it was touted as a "systems programming language" (which has been more or less silently changed since it wasn't true, but it doesn't stop the fans from parroting it). Same goes with many other false claims about the language, which aren't just non demonstrable, but demonstrably false.


Relax. Many people would call Docker/Kubernetes, and various types of databases like CockroachDB as systems. Lot of enterprises call large internal applications as "systems" There is no copyright on "systems programing" and it can be used only for OS kernels and such.


While I agree it's probably not worth getting angry about, I would agree with the GP that the word "systems programming" traditionally has a meaning that's different from "programming things that might be called systems". What you're describing is often more simply called "backend programming".


In that case, Python is also a "systems language", but you don't see people claiming it as such.


Then go ahead and do it. Who knows, it could dissipate a lot of your pent up anger.


You can stop with the odhominem


Java does not perform better than Go, they're pretty much on par but I would say that overall Go is faster. 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.

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.


> Java does not perform better than Go, they're pretty much on par but I would say that overall Go is faster.

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.


I urge you to look into Javas new ZGC. It's a massive achievement, having only <1ms pauses even on TB sized heaps. Go:s GC can't hold a candle to it in terms of low consistent latency.


> 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.

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.


99% of people run Java on the JVM. Just because you 'can' compile java to native code doesn't mean it really applies in real world situations. Plus as far as I can tell the only compiler that can compile Java to native code (GCJ) is no longer maintained.


PTC, Aicas, WebSphere Real Time, GraalVM, OpenJ9.

Additionally, JIT caches are now part of OpenJDK, OpenJ9 and ART.


Nowadays people use GraalVM Native image for AOT compilation.


It's true. Go is basically Java/C# with its runtime baked into the binary. Also, it's only marginally faster than 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!!!"


golang is not marginally faster than Java however in the real world.


Golang can usually more than halve your memory usage? The economics may dictate something more like golang.


What does that have to do with being "closer to the metal"?

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.

[1] https://quarkus.io/ [2] https://micronaut.io/


You also asked what the advantage of golang was and I told you what the advantage was. I'm was not saying "closer to the metal" is the right description, but, if we'd stop being pedantic, I think we know what she/he meant.

Some people really don't want to tune VMs. No one is saying Java doesn't have a place too. Jeez, dude.


OK -- but you've listed a whole bunch of third party projects and technologies and tools to get Java closer to what Go does _really_ easily out of the box.

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.


Golang + UI should have trash JDK to the trash of the history long ago, but people never learns, they are blinded by the Enterprisitis.

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.


Ahh, Golang UI stuff... now that is a dark alleyway!

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.


GraalVM is not third party.


What are those Java framework turds like Quarkus, that you keep bringing again and again?

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.


"Go performance is excellent since Go compiles to bare metal and there is no runtime between the metal and the language as is the case in other languages I used in this exercise (Java & Clojure: compile to JVM bytecode, and JVM runs the bytecode, Python & Javascript: interpreted languages and the runtime runs the code (though also Python compiles to bytecode)."

Source: https://www.karimarttila.fi/languages/2018/11/13/go-good-pro...


That statement is misleading. You can also compile Java natively. Secondly, there is most definitely a runtime "between the metal and the language" in golang, the GC being the most obvious.

This proves my thoughts that these claims are not valid.


>You can also compile Java natively.

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.


I never said it was rare or not. It is possible if you truly need fast startup times. The fact of the matter, the JVM is good enough for the vast majority of the use cases, and is faster than golang so no need to even go there.


>, and is faster than golang

Nice joke. Go software runs much snappier and faster than any Java GUI or CLI tool.


Not a joke. Large real world programs are proof (otherwise, my employer will not be spending millions of dollars on reinventing performance and other toolings already present in the JVM). What golang gui programs are you comparing against?

Java CLI tools compiled with GraalVM start up just as fast.


You weren't looking into the right plaaces, ExcelsiorJET, PTC, Aicas, Websphere Real Time, Android since version 5 (althought the story here is complicated).


>That statement is about as true as saying you can compile Python natively.

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.


So does TCL, and yet Go outperforms TCL/TK, even 8.6.


Call me when I can compile some static binary for Windows from OpenBSD such as GOOS=Windows GOARCH=arch32 go build foo.go.


There is a runtime between the metal and the language. The runtime is what makes it really nice for writing network services in. It's also what makes it really terrible for lower level things.


It wasn't claimed to be an advantage I think. It's a tradeoff.


A tradeoff in what exactly?


Shipping arch / OS / runtime vetsion neutral apps vs implementation simplicity and single file binaries, for one.

Also, decoupling of the runtime from the language is huge, and lets us have nice languages like Clojure and Scala on the JVM.


you aren’t closer to the metal than java


When I first read that, I thought the core conclusions were still mostly correct, but the delta claimed for Rust over Go was too large. Rust will certainly beat Go on this metric (I expect Rust to even beat C and C++ at scale [1]), but by a factor of 3 is probably on the large side.

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


> I have a couple of services that I consider trivial, but if I were writing them in Python, would be non-trivial.

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).


> 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.

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.


Yeah, this guy only had a superficial understanding of JS and Python--he had no idea how they worked under the hood, had no low level experience, no CS background, etc. We already sunk so much effort into the Python implementation that he couldn't get behind a rewrite in another language, and moreover I suspect he was intimidated by a "lower level" language (relative to Python) and figured it would take weeks or months to be productive.

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.


I guess this is the perfect use case for Nim. Same surface syntax as Python, so your senior dev would've had no issue surveying the code at least, even though contributing to it would've been more of a challenge (Nim compiles straight to C; it's not nearly as high level as either Go or Rust).


Probably, although I suspect Nim wouldn't have been a good choice because we were deploying on Lambda and we were using a lot of libraries for which there probably aren't good Nim alternatives (e.g., various cloud databases).


Rewrites are usually a hard sell. Their benefits are most of the time clear only to those who initiate them. The from/to languages hardly matter to bystanders, sadly.


And in general, rewrites often are a bad idea. But Python is hundreds of times slower than languages like Go and it really doesn't have good levers for improving performance--the "rewrite the slow parts in C / multiprocessing / numpy" promises rarely pan out due to the overhead of pickling / marshaling to efficient C representations. Unfortunately, our principle engineer lacked any mechanical sympathy--he could write JS and Python but had no idea how V8/CPython worked under the hood or how other languages might compare.

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.


Go is at about the same abstraction level as Java, maybe a fraction slower due to lack of runtime VM optimisations.


That's about right in aggregate, though some of the specifics go either way. I have two theories why so many people are convinced that Go is much lower level than it actually is, and I'm not sure which one is true:

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?


There's a couple of things that make Go feel lower level than Java. The first is having access to pointers and the manipulation that brings. Even though there isn't pointer arithmetic, needing to think about referencing and dereferencing structs is lower level than Java, which doesn't provide this in the language at all and forces all objects to be pointer-heavy references of references (apart from primitives).

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.


If these are really the two sticking points, then Go should be on roughly the same abstraction level as C#/.NET (which also has pointers - with arithmetic, even! - and user-defined value types with explicit layout control and unions).

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.


C# is an interesting case. You can only use pointers in unsafe C# code, so you could argue that unsafe C# code is as low-level as Go or even lower (which I actually think you'll find many supporters for), but unsafe C# is different from normal C# and is outside the purview of everyday C# code (whereas again pointers in Go are absolutely pedestrian).

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.


With C# and .NET, you also need to consider the different kinds of pointers involved. The thing that's closest to Go's pointers would be managed references (which don't have pointer arithmetic but are GC-aware). In C#, this corresponds to in/ref/out parameters, locals, and return types. Now, the difference is that those can point directly to the stack, and thus cannot escape the function in which they were created for memory safety reasons. But conversely, they are allowed in safe code.

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.


refs (and their various flavors) aren't really pointers though. They're a higher-level abstraction on top of pointers. For example, you can't have a ref of a ref, nor can you use a ref as an independent type, whereas both those things are possible in Go. refs, as their name suggests, are basically higher-level annotations to allow for pass by value or pass by reference, whereas pointers are lower-level independent types (usually in languages which only support pass by value). The point being here that references are a higher-level concept than pointers since you usually will use the latter to implement the former.

> 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.


> refs (and their various flavors) aren't really pointers though. They're a higher-level abstraction on top of pointers.

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.


I never got around to replying to this, but on the off-chance you're still following this thread (and any other readers still reading)

> 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#.


You do have more precise control over the memory layout of your data in Go. Because of the value types and support for using interior pointers to arrays. In that respect it's like C which is lowish level.


You can know your class alignments in Java using tools like http://openjdk.java.net/projects/code-tools/jol/

Having access to pointers is a superficiality. Java is getting value types, and C# already has them.


Right but those tools are VM-specific (e.g. the one you've posted is Hotspot-specific) and VM-specific optimizations are usually considered advanced optimizations in Java.

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.


> 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.


> If the end result is the same, then the claim is really meaningless.

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.


The point is, pointer dereferencing in golang is limited to mimicing value types/reference types, something that C# already offers. It is not like C and C++ where you have pointer arithmetic. So it's just an illusion that it is "closer to the metal".


No it's not. Value types and reference types are definition-site concepts whereas pointer referencing and dereferencing are call-site things. This allows both for different ways of programming as well as different language semantics.


> It is not like C and C++ where you have pointer arithmetic.

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!


Calling unsafe is not the point here. You can do similar things in Java if you really need to.


It's also worth distinguishing between "has value types" and "uses value types". C# has them, but they aren't particularly idiomatic--they're regarded as an optimization.


They're reasonably idiomatic. Anyone dealing with the standard library will likely have to deal with KeyValuePair, for example - which is a value type. So are the (language-integrated) tuples.


I take your point--idiomatic C# code uses some value types. It would be more accurate for me to say there's a difference in degree with respect to the extent that C# and Go employ value types: wherever C# uses classes, Go uses structs; where C# uses class instances, Go uses either struct values or pointers to struct values (or interfaces, in the case of runtime polymorphism).


Tuples are a great example that I might not have thought of -- you practically "define" your own value types all the time. Also, all enum types are value types. And there are lots and lots of very commonly used value types in the base class library: DateTime, TimeSpan, Guid, CancellationToken. Programmers have to be familiar with their semantics.


Yeah, this makes sense. I concede that idiomatic C# uses value types sometimes, although they're more pervasive in idiomatic Go.


How is that relevant to the point whether a language is "closer to the metal" or not?


Because calling a language "close to the metal" is potentially ambiguous--it can imply that a language merely has "close to the metal" features or it can imply that they not only exist but are used in the course of idiomatic code. Specifically, only one of these interpreters conveys the idea: "I can read code in this language and have a pretty good idea about what the generated machine code is like".


> I can read code in this language and have a pretty good idea about what the generated machine code is like

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.


First of all, you can look at C code and have a pretty good idea about the generated machine code, or at least a much better idea than with C# and Java. Secondly, it's even easier to predict Go code because Go compilers don't optimize as aggressively as C compilers and because Go has very little UB.

> 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.


> Java is getting value types,

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?


The philosophy behind Java is that a feature needs to be demonstrated in production very well before it is added to the language, in order no to go overboard with features and end up with C++ :)

Value types have proven their worth, especially with the gap between CPUs and memory bandwidth, so they're being added.


Go has a C style error handling that by virtue of being very bad, composes a large share of the code one writes. That pushes people impression of it near the one of C, even though it has some very high-level features.


I agree completely with your reasons. Generally, people just have no idea what is going on.


This is not our experience at all.

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.


> and spend a lot more time doing GC and other unproductive work than typical Go programs

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.


Java GC has to be very good considering Java could take 10 times more memory for typical data structures compared to memory efficient layouts.


Not sure if sarcastic, but that has nothing to do with the quality of a GC algorithm. Sure, Java does not (yet) have value types so data structures with non-primitive elements will not have the most optimal layout, but java does objectively have the state of the art GC implementations that work under so high allocation rates that other GCs simply break down. Eg. Go notably slows down threads to let the GC catch up with the work load.


Go is "lower level" in the sense that it depends much less on the JVM to optimize things for you. Go code maps much more neatly and predictably to machine code because the optimizations are fewer and less aggressive (e.g., Go has semantics for memory layout). This doesn't imply that Go is inherently faster than Java, but it's easier to reason about the performance of Go code.


It's slower or faster but certainly not overall slower.

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.


Go can be much, much faster if fast compile times is not focus. A good compiler can probably do some compile-time RAII inlining like C++ and Rust and reduce GC usage without needing any major changes to the core Go language. A lot of the more advanced optimizations done by LLVM are not implemented in Go because they slow down compilation. Go compiles insanely quick (like classic Pascal/VB), it feels more like a JITed language than compiled.


Does RAII make code faster? I thought RAII just meant things were freed as soon as they went out of scope rather than waiting for the next GC sweep? Seems like this would make code slower (instead of one free() for all garbage, you have to do a free() for each allocation). Granted, the tracing burden is lower, but I'm of the impression that tracing is cheap?


> Go compiles insanely quick

Compared to Java?


He's implying that it's comparable to Java in that it compiles fast:

> it feels more like a JITed language than compiled


Yeah, and one need to compare `go build` with `mvn clean compile/package` not javac. Because Java does not come with inbuilt tools to compile whole projects.


Too bad Java doesn't have an option to disable all runtime type reflection, etc... it would be very similar to Go, I bet. Especially if AOT compiling was used.


Benchmark sites really need categories like speedruns. The finish line is something like "takes a dynamic input and gets the right answer". Then you have categories that do it under some additional constraints:

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.


Something like leetcode is the perfect platform for this kind of data; crowdsourced submissions for each language, with automated I/O systems to verify accuracy. In fact, when you submit a solution to a given problem, it tells you your completion time and memory usage percentile relative to other submissions in that language. It'd be very cool if they published statistics for each problem, for each language, but I'm not seeing that data right now.


Indeed, fast code in an idiomatic style doesn't mean it wasn't optimised for that benchmark. The only code I trust is code written by people having no idea about the benchmark.


Each speedrun community decides their own rules. The people in the community are best placed to decide what it is exactly they're comparing and why. So there's no comparison from one community to another. But such comparisons are exactly what we'd be talking about for programming languages.

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.


You found it, "idiomatic" is the most subjective of all the potential categories I listed. "idiomatic" may not be useful to compare different languages, but it could still be useful for comparing libraries and frameworks in a single language. If it did work across languages I would expect Perl programmers to decide amongst themselves what is idiomatic, and be willing to explain their reasoning to others (including Rust programmers).

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.


See, with speed runs we're comparing players - people - on the charts. There is no chart about Grand Poo World 2 versus a Quake Total Conversion, the chart is like, OK, Poo is very good at GPW2 and Panga isn't as fast, but Tofu is faster than Poo.

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.


> language% (anything possible using just that language)

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? ¯\_(ツ)_/¯


I agree with rsc about that study having problems, [1] but I'm not sure this part is accurate:

> 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...


The Discord and AWS posts are both about GC vs no GC. The GC in Go prevented scaling the cache due to latency spikes. Rust doesn't have a GC and so the cache could be grown from x million to 8 million without increasing latency. The Discord post says just simply rewriting the service from Go to Rust increased performance in all metrics (but doesn't give numbers).

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.


Switching from hash table to B-tree is another example of something Rust is good at and Go 1.10 wasn't, so I'd call it a fair part of the comparison. That version of Go doesn't have generics, so you're stuck with the builtin hash tables, reflection, or code duplication/generation. (Also, I'm reading the post correctly, this switch was included in the first set of graphs that he called "roughly comparable").

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.)


> SIMD intrinsics replacing naive code

The programs that use SIMD intrinsics are split-out:

https://benchmarksgame-team.pages.debian.net/benchmarksgame/...


That's great! If you had that back then, though, they didn't take advantage of it...


The 2017 study was completely independent. The authors selected programs that matched their criteria.


Nitpick: Rust comes with GC in the std lib: https://doc.rust-lang.org/book/ch15-04-rc.html


> Nitpick: Rust comes with GC in the std lib: https://doc.rust-lang.org/book/ch15-04-rc.html

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.


> Most obviously, if your study claims that C++ uses 34% more energy, 56% more time, and 14% more memory than C, it’s time to reexamine your assumptions. Approximately every C program is a valid C++ program, so C++ can’t lose, especially not that badly!

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.


Never trust a benchmark you didn't falsify yourself.

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.


> "Never trust a benchmark you didn't falsify yourself."

Stealing this.


For what is worth, I stole it myself ;)


I am wondering if James Kanze is the source:

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...


Indeed he is, from comp.lang.c++!


We can do better than "every C program is a valid C++ program, so C++ can’t lose" defensiveness.

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.)


Agenda-driven benchmarks and decision-making benchmarks are dramatically different things.


Russ argues that since you can compile lots of valid C programs as C++, surely C++ "can't lose" but this is a revealing error.

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.


It is also not necessarily true that a C compiler will be better than a C++ compiler at optimizing the same code. Even more so considering that both gcc and clang are basically C++ compilers regardless.

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.


C++ has more complexity not just more information. These comparisons are always flawed because they vary not just by language but by expertise of the one writing the benchmark in each language. And I think C++ has more potential for writing suboptimal code due to its complexity. It's also not obvious that all the different c++ constructs help the compiler speed up, IMO.


> … vary not just by language but by expertise…

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.


Don't clang, gcc, and msvc share a bunch of code between their C and C++ frontends (and uh, share the backend).



I would be that compiler differences are trivial. Especially since all C compilers are also C++ compilers. You may just have tiny different language semantics that may optimize differently.

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.


That's odd because in my circle it is generally recognized that C++ idioms are more efficient than C. Sometimes dramatically so. The canonical example of course is std::sort vs. qsort, where C++ wins big. But also anywhere dynamic allocation is called for, the efficiency gains of new/delete over malloc/free are going to become apparent.


> the efficiency gains of new/delete over malloc/free are going to become apparent.

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.


C++ has placement new and custom allocators for this. Rust actually has a harder time doing these things, though local allocators are in the works for Rust too.


My C++ is very rusty (pun not intended), I had no idea you can plug in a custom allocator but still use new/delete for initialization. Do you maybe have a link to something where I can read more about this?


The C++ new keyword is just an operator and so you can overload it like most C++ operators, to do whatever you want. In fact it's not just one operator, it's a whole family of them.


In my opinion arena allocation is much more idiomatic, and more commonly seen, in C++ than it is in C.


> the efficiency gains of new/delete over malloc/free are going to become apparent.

What are those?


I mean, there shouldn't be any, given how many C++ standard libraries implement `operator new` on top of malloc. If anything it should be less efficient once you factor in constructor calls.

libc++: https://github.com/llvm-mirror/libcxx/blob/master/src/new.cp... libstdc++: https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-...


It is common in C++ to override operators new and delete. When you use tcmalloc in a C++ program it overrides all the global allocator and deallocator operators. When you use tcmalloc in a C program, it just overrides malloc, because there is no richer API available.


And the same overriding can be done in a C program with malloc and free, too.


new, when the compiler can see the definition at build time and with known size, may compile down to as little as two x86 instructions without a call. That's not generally true of malloc. C also lacks an equivalent of sized delete, which is very efficient. In C the implementation of free has to figure out what size ptr represented.


> new, [...] may compile down to as little as two x86 instructions

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).


With all due respect, that is trivial to do in C++, hell it will be actually readable and much safer than malloc(n*sizeof(something)) and then for looping over that to initialize values.


Is that some sort of allocation elision where it's actually on the stack? Or more to the point, what are those two instructions?


I took it to mean idiomatic C++ features make it slower than C, but now I am not so sure. I would like to see some example programs with that delta, it is surprising even if that was the reason.


I think he means that if you keep optimising a C++ program, or certain part of it, you eventually get to something very close to C, thus with similar energy and memory consumption. But indeed that's the problem with that kind of comparison - what's being compared exactly? How optimised is each program?


No, I think RSC means that C89 programs are literally valid C++ programs, and more recent C dialects nearly so.


But C89 programs aren't literally valid C++ programs, either. Although I can't see any reason why those differences would affect performance.

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).


The versioning scheme used in Go may be somewhat misleading to outsiders. There are often significant runtime changes between versions, but only the minor version is bumped. For example, adding generics to C# (among other things) prompted the C# team to bump the version of the language from 1 to 2. Go adds generics in 1.18, and if you don't follow the news, you won't know it's not just a minor improvement over 1.17, but one can say a whole paradigm shift (we've been waiting for generics for 13 years). So perhaps many people think there's nothing wrong about using old benchmarks for Go 1.10 because it doesn't feel like a lot must have changed between 1.10 and 1.18


I would expect major versions to only increase when backwards compatibility is broken, not merely because the runtime performance improved. Go's version scheme makes a lot of sense to me.


That was one of the reasons why by Java 9, the dual versioning scheme, e.g. 8 vs 1.8, was abandoned.


Go preserves major version bumps for things that can break compatibility. It's an engineering indicator, not a marketing tool.


Actually it more like the religion decision that Go 2.0 will never happen.


Marking very major new features is also an engineering indicator.


Go preserves major version bumps for things that can break compatibility.


It does. I'm not saying that choice is wrong at all, I'm saying that particular choice is a matter of engineering vs. engineering and not engineering vs. marketing.


Sun's version scheme choice was a marketing one.


The way your post was written I thought you were comparing to both Java and the idea that Go could have called 1.18 2.0


Sun did the same with Solaris’s versioning, jumping from Solaris 2.6 to 7 (i.e. 2.7).


I'm really glad to have Rust and Go. I was worried the only way out of startups using scripting languages was Java or C++.

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.


Exactly! We are spoiled for choice.

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!).


Alternative way to read the Twitter thread: https://nitter.net/_rsc/status/1496352325157457922


That is a lot cleaner than the original. I still prefer https://threadreaderapp.com/thread/1496352325157457922.html though which removes almost all of the chrome between sentences.


To what extent are the resulting co2 emissions of a program linked to its runtime resource use? On a succesful SV "web scale" app they are no doubt notable, but for 99.999% of programs the costs of getting it written surely dominate. The CO2 footprint then comes from the resulting consumer spending and other downstream economic activity, quantifiable by carbon intensity of the economy ($/kg/CO2e). [1]

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


The original post felt like when companies like Coca-cola tell you to recycle and act like the burden is with the individual instead of the massive corporation. Since this data was either posted (or at least referenced, can't remember) by AWS, it seems fitting. Amazon couldn't possibly have the power to deal with these rising emissions, it's the code that our customers run being written in Python that does it!

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.


AWS has huge incentives to optimize their infrastructure as much as possible. One that's done, all AWS resource consumption is on behalf of their customers.


I don't know. This reads more like a case of sour grapes. The discord article clearly states that some of the optimisations (like data structure improvements and especially the larger cache bucket sizes) weren't possible at all due to the way Go's garbage collection works (scanning for the entire structure rather than few changed bits). May be that improved with the latest Go version? Even if it did, it's not possible to have the same predictable performance from any GC languages for that particular use case which seem to be suited for a non GC language.

Fair points about c++ and the overall deficits in the original study that doesn't use comparable programs across languages.



Really comes down to "Do you want GC spikes or not?" and the tail latencies that go with it (clearly visible in the images in the tweet thread). The alternative, before Rust, was to use C or C++, and I'd never consider writing a Java or Go service in C++. Rust doesn't just bring safety to C, it brings an ecosystem that brings a "system language" up to the point where I'd consider it for general services.

Edit: deleted java vs go comment, and replaced with the evidence at hand


Java is definitely not faster than Go.


Real world programs say otherwise.


The benchmark in the tweet show it is.


A link back to the post so folks who haven't read it thoroughly can follow along: https://aws.amazon.com/blogs/opensource/sustainability-with-...


The numbers are also very suspicious with JavaScript and TypeScript by the way.


The data tables published with that 2017 paper, show a *15x* difference between the measured times of the selected JavaScript and TypeScript fannkuch-redux programs.

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 --noEmitOnError


No one transliterated those JavaScript programs into type annotated TypeScript that passes type check. Different TypeScript programs were contributed.


Has anyone done a separate study to corroborate, contradict, or otherwise update the work of this 2017 paper?

https://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.


Indeed! This followup used sample code from the Rosetta Code wiki:

https://haslab.github.io/SAFER/scp21.pdf

The rankings move a little bit, but the relative positions of Go and Rust don't.


On page 258, the 2017 paper tells you where to find that information:

    The measuring framework and the complete set
    of results are publicly available at 

    https://sites.google.com/view/energy-efficiency-languages


> Approximately every C program is a valid C++ program, so C++ can’t lose, especially not that badly!

He 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.


Meanwhile we can do better than "every C program is a valid C++ program, so C++ can’t lose" defensiveness.

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.)


fwiw "… nontrivial C programs will not compile as C++ code without modification."

https://en.wikipedia.org/wiki/Compatibility_of_C_and_C++


The project CONTRIBUTING.md says —

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.


I really don't see how it's misleading. It shows their rust implementation after some tuning vs their go implementation, which I'm sure was tuned as well.


This doesn't surprise me. Rust's toxic community was one of several reasons that made me turn away from the language. When people ask on a Go forum (e.g. r/golang) whether they should switch to or write a project in Go, they tend to get good and professional advice, and often this advice is to stick with the existing language and approach. I don't know why this is so, but with Rust you mostly get zealots with zero ability to evaluate the Pros and Cons of their language of choice.


That's not really consistent with my experience in the Rust community at all. Memes aside, rewrites for the sake of rewrites are typically discouraged. When language comparisons are made, they tend to clearly highlight objective and subjective pros/cons.

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.


Rewrites for the sake of rewrites is a HN thing. This used to happen with other languages too. In 2015, Front page posts on a given day would have "xyz in Go" and it was JavaScript before that. People tend to reimplement systems they know in languages they are learning and it's perfectly alright.

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.


Rewriting a small thing you know well is a fabulous way to understand the new language, because your focus isn't on "what should the code do" but "how do I make the code do this thing."

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").


> 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


I wouldn't actually characterize "Wordle in Rust", "xyz in Go", etc for the sake of learning a language as "rewrites for the sake of rewrites", though - I would characterize them as rewrites for the sake of learning and there's nothing wrong with that. Coming up with a new idea and implementing it with technology you don't know is a lot harder than implementing an existing idea.

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"


"In 2015, Front page posts on a given day would have "xyz in Go" and it was JavaScript before that."

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.


In my experience, the Rust community isn't toxic at all. To the contrary: when I started to explore Rust, it was very welcoming, helped finding the answer to all my nooby questions, which they probably heard a dozen times - despite having a lot of C++ and JS baggage


Did you see what happened to the actix project. That was embarrassing


What happened to the actix project?

Asking, because I use it daily :)


It used to use a bunch of unsafe, someone decided that wasn't acceptable and it blew up into a whole thing where people started dog piling on the project and the maintainer. everyone got emotional, he eventually quit and gave up actix to someone else i think. pretty unnecessary and childish the way people acted towards him imo. a lot happened on reddit and github so it should be easy to find.


As I recall, it wasn't just that unsafe was being used, plenty of libraries use unsafe, but that there were multiple examples of unsafe being used with provable soundness issues when it was possible to do the same with safe code and no performance loss. On a web server which the author was advertising as production ready.

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.


He didn't deserve any of it. They felt entitled to a better code he didn't want in his codebase. I remember the shitshow, many people are too entitled, rust community is not exempt of them.


You have a veery one sided perspective. Someone adopting/learning a language will be met with plenty of positivity and encouragement. The toxicity comes in to play once you post something critical of the language, even if it has merit.


So you're saying that people leaving comments on (I assume) blogs are typically less civil than people who take the time to work with newcomers and actually make the community work?

That is certainly true, but for pretty much every community that I know, not limited to any programming language or technology.


If you read the full context, the misunderstanding comes from research not done by the Rust community. You just found a way to reinforce your own bias.


I think Rust has attracted a lot of people who don't really program in it often, but try to advocate for it. My experiences with the community have always been good, and they are consistently rated as having a really great community, but a growing community will always have issues.


I’m fairly certain this is the case. The people who seem to want everything rewritten in Rust are not necessarily also people who actually use the language. They likely just heard that it improves security and stability to use Rust over C. Would be similar to the people who bring up Ada as an alternative; the chances that they’ve actually tried it are minuscule.

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)


You can use the language and ignore the parts of the community you don't like. I've made similar observations, but still conclude the language is top-notch.

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 .


I'll say as someone who leans more towards Rust, and its community, in this somewhat manufactured debate -- I think it's pretty hard not to be a little prickly when most of the criticism amounts to "I hate the hype!" or "I don't like change!"

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.


(to complete what mustache_kimono wrote)

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.


I hate the hype. I like honesty, pros vs cons. Rust community seems full of zealots and evangelists. Those people are very vocal about pros, and condescending. As soon as someone brings any con or isn’t convinced, they are just bad programmers, with bad arguments, and even « embarrassingly flimsy » criticism…


Re: the hype, I don't know what language you use without hype behind it, but what I say is -- there are worse things than a little hype. Java had millions of dollars poured into a marketing campaign, and it has done pretty well for itself. Rust's hype is mostly grassroots, and, from my perspective, the excitement behind Rust is reasonably well-justified for many software domains.

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!


What about honest hype? I would tend to believe there could be such a thing. Maybe it doesn't make sense. Let me try. "Rust offers strong memory safety guarantees without the usual runtime overhead of memory safe languages. Ease of development tend to suffer, but it's the best choice when both security and performance are crucial."

Not sure I convinced my self here. But yeah, something like that.


As someone who uses Rust daily, I actually find that my ease of development increases wrt Python or JavaScript. I spend less time writing tests for silly edge cases because I can easily make sure that these edge cases can't happen, less time wondering whether I really have covered all bases, less time recovering from crashes in staging environments (or, in at least one case, in production) caused by me failing to cover all bases.

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.


I think that is a fair summary, but if I can speak for why I personally enjoy Rust, it's because I don't find

>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.


I think hype is just something we live with. And I think that the anti-hype hate is just as annoying as the hype train.

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.


I think Rust has grown way beyond the point where it makes sense to characterize the community as a whole. I imagine you could get entirely different experiences depending upon whether you were engaging with people in some HN thread, or Reddit, or IRC, or Discord,...and even day by day or thread by thread.

Where specifically did you encounter the toxicity?


Every interaction I’ve had with the rust community was toxic in nature. Also for whatever reason all those „rust vs go“ articles are always coming from the rust corner. It’s like the rust community is frustrated about go success and they individually feel the need to attack their perceived competitor meanwhile the go community has no issue with any other language.


That's odd.

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.


Well then it might me a reddit thing because I've had both interactions on r/rust and in the comments of r/golang where rust fanboys derailed the the conversation


Oh, reddit!

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.


[deleted]


I don't get the logic of dropping a language you find great and you are productive in because of a few people


I removed my comment since it wasn't being constructive here, and I realized it was off-topic for this post.

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.


Those people will (potentially) be your future co-workers... So it is a valid concern.

That said, I have not interacted with the Rust community and cannot make any comments in that regard. Language is pretty nice though.


You'll eventually have to cooperate with those people, whether it's on OSS or in the workplace.

It's even worse when really senior figures in a community are also the source of the toxicity.


Its weird, because the negative stuff I see around the Rust community is posts like this with name calling or histrionics, and the positive stuff is like 'thoughtful post mortem about why Rust compile times are so bad (but are improving)'.


To some extent I agree with you, I don't like the rust community generically, as I feel the toxicity too, but I mean the language and the offering of a fast but easy and checked platform to be are still good enough to let me just ignore them, I mean on IRC you always find people who are going to be bad, and people who will just help, and who cares about those who are bad, I guess just feel a few seconds of pity for them and move on


Out of curiosity, where did you see any toxicity?

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.


Really? I've never found a community as nice as the Rust one.

Where did you ask?


Painting an entire community with the same brush seems to itself be fairly toxic behavior berift of signal.

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.


> Painting an entire community with the same brush seems to itself be fairly toxic behavior berift of signal.

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.


I don't know which community is toxic and wouldn't even risk using such strong term.

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?


> just for the sake of pride(?).

It's to prevent all memory safety bugs.


Partly true, but this doesn't apply to many rewritten tools, like cat or ls where memory safety bugs are tolerable


Doesn't sound like things where you'd want bugs either. But also the rewrites for this kind of thing are usually not that "serious". A few people do it because they can or because they think it's interesting, but it's not like there are large pushes to replace such tools (E.g. there's been "lets rewrite basic tools in Go" projects too, and nobody is surprised that they aren't in widespread use"). The winners usually are not in that category, but tools that stand alone.


Well, I don't know about the cat command (though that _is_ appropriate for something like the Redox OS, which aspires to be a pure-Rust desktop operating system).

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.


I see no evil here. For many it is probably just a hobby project they can practice their rust skills on.

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.


And to increase binary size by 5x.



The Golang fanboyism is getting out of hand. Rust may not be the flawless language zealots tend to believe but at least it has something new to offer that was missing from the PL ecosystem and many other features that the industry didn't take seriously till lately cause they were "only for research languages".

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.


The people who created Go had vast amounts of experience in creating new useful things. It's not their first system or language. For example, they created unix and utf8. Please give them a little credit for their experience and time trying to build things.

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.


Programming languages are there to make programmers productive

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.


Just because they created an OS and a character encoding does not mean that they are automatically good at creating programming languages.


nor did he say it did. the point of listing their credentials was to request a more sympathetic ear. if you're intent on seeing only the bad side of go, then no one will be able to convince you. taking a sympathetic perspective is required to truly understand the language and its utility.


I've used both in production settings, a sympathetic perspective doesn't help :)


As have I, heavily, and it did.


Go wasn't their first programming language. For example, one of them started the B language. Another one of them created Sawzall, a domain specific programming language.

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 have, and it proves my argument. Limbo went nowhere when it was developed under AT&T for example.


Can you tell me anything novel about go? It’s not even a novel combination of features.


Since when is novelty the cardinal virtue of a programming language? In my book "usefulness" is more important than novelty for professional dev work (the opposite is true for hobby work, typically).

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.


Here's a comment I made ~four years ago regarding what I consider to be the combination of essential features that no other language has, and it's only gotten better since then: https://old.reddit.com/r/ProgrammerHumor/comments/76k7x1/whe...


I'm really curious: the article cited at the end of the post [1] lists major projects that use each respective language.

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...


> 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.

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...


> What do you make of the fact?

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.


>Golang has been marketed heavily by Google

Where is this marketing by google? Though I don't really understand the point of this line of questioning anyways.


> Golang has been marketed heavily by Google,

Sounds like bullshit to me. Go did not even have proper Google branded website until last year.


I don't know about others, but I started looking at Go in 2011-2012 because it was a Google programming language. Maybe "marketed heavily" is a poor formulation, but every developper knew it had Google behind it and that came at a time when Google still had a reputation of succeeding at everything they attempted.


It doesn't matter; everybody knows that Go is a Google project, and is not going away for as long as Google is around, simply because they have so much internal code written in it.


Exactly. In my work environment, Go was a serious tool from a big serious company and Rust is a hyped up hobby language without any serious funding or support. For a new project, the guidelines from above were "any language you want except Rust".

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.


FWIW this was not a remark on relative values of Go and Rust, but rather public perception of them, and how it affected their adoption rates. Personally, I'm mildly positive on Rust, but view Go as a step back in PL evolution.

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.


Rust is a lot newer, for one thing. It was arguably not even generally usable prior to the 2018 edition (which added 'non-lexical' lifetimes). Go has had practically no major changes since its 1.0 release in 2012 or so. Yes, it has generics now but these are very new still.


Rust is newer and had less time to gain major projects, Golang was pushed harder into companies (I.e. I feel like I saw it adopted for work stuff earlier relatively speaking, compared to Rust where most of the enthusiasm I see is hobbyists), Rust's main (not only!) point of targeting C++'s domain is a lot more conservative and difficult target space than Go's. And in the overlap Go might have "captured" some projects that would've picked Rust if Rust had been the earlier one.


Simple: hype. I've seen it at an employer who heavily uses golang. Even a very high up key person in the org was not able to properly defend why the company was using it.

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 firmly believe that the only reason golang gained traction and got to the point where it is today is that it had the Google branding behind it.

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.


How is the design too simple? Why is complexity better?


There's plenty of "surprise" complexity hidden in the Golang stdlib. https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-... It's kind of a necessary evil (though I guess it should be better documented, so users are less likely to bitten by it) because Go does not have good ways of expressing complexity in interfaces. This is exactly how enforced "simplicity" can backfire in practice.


But this is a good thing not a bad thing. The mistake in that article is thinking that the existence of surprising edge cases means the whole design was a mistake. Its better to design for the 99% not the 1%. I don't want to live in Amos' perfectionist world.


It is a mistake when the "1% of the time" edge cases are "surprising"; it's not a problem if they aren't, because the interface accounts for them properly. "Surprising edge-cases" in general is a recipe for very hard to debug software defects.


I think I disagree. I think its ok to make the 1% use case require extra work to debug (it was probably hard anyways) rather than making the 99% pay a tax on every use of the API to save them. But this is perhaps also where the question becomes more of a question of values and use cases than something generalizable. I don't think Go is the ideal language for writing code that demandingly low-level.


Interesting. I come from the opposite point of view, i.e. that surprising behavior should be avoided like the plague, using combinations of type-checking or linting or dynamic detection (in debug builds).

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.


I write Java for a living. In my opinion the largest source of bugs and general misery is the complexity of our codebase which, in my opinion, is sourced to Java community philosophies in Clean Code. There is far too much abstraction, and far too much emphasis on making sure code does everything/is flexible as opposed to making sure code does the thing it is meant to do. As a result the amount of the codebase any one can understand is much smaller and people make bandaid fixes rather than deep fixes. Having a shallow deeply understandable code base is crucial in my opinion, and having APIs that are extra complicated and hard to use makes it much harder to use them well/make changes.

The book "A Philosophy of Software Design" has been formative for me.


Yes, that is a common complaint against Enterprise-style Java code. It's funny because when I started Java (shortly before 1.0, iirc), Java was the clean new language on the block and it was so much more readable than just about everything else used in the industry!

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.


"Enterprise code" can be written in any language. My employer invented an entire web framework in golang and we routinely see stack traces up to 80 lines or more even. A lot of the need to write such a framework was from golang's shortcomings.


Golang is anemic when it comes to modeling ability and expressiveness. I have come across so many code sections where having something like sealed classes and pattern matching or switch expressions would have made code so much readable and safer.

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.


>Golang is anemic when it comes to modeling ability and expressiveness. I have come across so many code sections where having something like sealed classes and pattern matching or switch expressions would have made code so much readable and safer.

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.


There's a balance. Reading golang code is very burdensome because the reader has to read many lines of code which can amount to nothing more than a map/filter/reduce, and those are littered everywhere. Same with error handling, which is pervasive and distracts from what the code is trying to do.

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.


>Reading golang code is very burdensome because the reader has to read many lines of code which can amount to nothing more than a map/filter/reduce, and those are littered everywhere.

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.


There's no reason to have to write overly abstract code in Java. Modern Java has many features that let you write even more clear code (e.g. interfaces with default methods). Even things like mocking, you don't need to write a dedicated interface, unlike golang, which clutters up the code base and makes it difficult to follow.

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.


> There's no reason to have to write overly abstract code in Java.

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 agree, though will point out that one other factor in go’s favor besides fast compiles is relatively easy cross-compiling to other architectures. At least back when it came out, that was a noticeable feature to folks evaluating it.

But yeah it seems to be more of a competitor vs Python and Ruby than vs Rust.


To be fair, the combination of cargo and llvm makes cross-compiling just as easy in Rust. But yes it's a great improvement over clunky old C/C++ toolchains. Go is not a bad language after all, in fact it has plenty of things going for it.


I think a big factor in Go adoption was exactly this. If you wanted a language that could be used for system-y programming (i.e. something that AOT compiles to a native binary) then Go was the only game in town (before Rust came onto the scene) aside from C/C++. And the tooling for C/C++ development is pretty atrocious. I hadn't done any C++ dev in a long time until relatively recently and was pretty shocked to see how bad it was. It's amazing that there is still no reliable way to manage dependencies and the various C++ package managers out there (vcpkg, conan, etc) are still extremely finicky.


I don't even get why Golang is considered a good language.

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.


The 'get shit done' feel of Go and the like is quite illusory, because you're left with a mountain of bugs and general technical debt to be addressed. Rust is designed to 'get sh!t right', where once it's "done" there's at least a fighting chance that it's really done and can actually be delivered in production.


I've had two servers I wrote in Go go into production (and a variety of command line tools, and a library used in other servers). Having code that is effectively bug free is the norm. I think one time I had an FD leak that took a few days to show up (bad TCP connection state handling in my code, not really something that Rust would avoid, I guess), but every thing else was feature requests (and sometimes fixing bugs in the new feature's logic, but that's sort of different).

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.


> you're left with a mountain of bugs and general technical debt to be addressed

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.


I haven't done any serious go, so I cannot debate this in depth. However, seen from a distance, it feels like:

- 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.


> people who code in go and hate it do so because their code is buggy.

My experience is that these people hate go because it's less expressive and more repetitive. See `if err != nil` as an example.


I haven't heard much from these people, but perhaps their anecdotes are simply less funny than bugs in go :)


I don't know about full, but when the correct way to add an item to a collection is:

   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.


This strikes me as very shallow. I would grant that this is an unusual pattern, but I don't think it follows that the language as a whole is poorly designed because of that. It's simply a different design, not necessarily a worse one.


What is the advantage of this design? Its disadvantages are quite obvious, but what use case do they buy?

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.


It seems weird enough for a language that's supposed to be "simple" and "easy to learn" in the first place. Note that a lot of the not-so-intuitive complexity in the Rust borrow checker is intended precisely to avoid having code be affected by lower-level details such as storage reallocation.


I agree, it is not the easiest possible api to learn, so perhaps there was a trade off made. Concluding the language is bad because you don’t understand a decision is lazy.


Well, if a call such as `append(...)` (without the assignment) can either succeed or fail silently depending on side-conditions, this specific example seems to suggest that a developer cannot trust the result of tests.

That... doesn't feel me with much confidence in the language (or, well, its stdlib).


This is again just too shallow. I suggest you investigate the reasons why it was designed this way before deciding it is bad.


I'm certain that there is a valid reason. Nevertheless, it feels like a footgun and that footgun will find itself embedded somewhere deep in code I didn't write. If it can explode in my face despite careful testing, I'm not really happy about that perspective.

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.


Given that the data seem to suggest more code is written in Go, perhaps it's just that code is in general buggy and that is a hateful thing, but it means the language isn't getting in the way also.


> Given that the data seem to suggest more code is written in Go, perhaps it's just that code is in general buggy and that is a hateful thing

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.


What's your definition of a good language?

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.


Go users also make similar claims, it's just that their definition of "best" is different (e.g. valuing simplicity over generality or correctness).


I think classifying the post as "fanboyism" is a bit harsh. The blog presented figures in a misleading way. Seems fair that he should be allowed to respond and correct.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: