BUT Go offers simplicity, incredibly easy concurrency, decent performance, a good standard library for networking/http services, very quick compile times and a stricter type system than many other languages like Python/PHP.
For many teams, especially coming from more dynamic languages, Go fills a sweet spot of performance, concurrency, type safety and ease of use.
I love Rust, but it is much more complex, boilerplate heavy, adds more friction, is harder to learn, and concurrency is much harder to get right, even with the upcoming async/await feature. Not to mention the immature ecosystem, that will still need years of catching up to achieve parity with Go.
Kotlin is also a great language, but has it's own downsides. You need to learn and understand Java (due to the many Java dependencies that you will pull in), there often will be a mismatch between Java libraries and idiomatic Kotlin code, concurrency is harder than in Go, and the JVM is a much more complex beast then the minimally tunable Go runtime.
Go definitely has a place, even though it often wouldn't be my own first choice.
I’m totally biased because I’ve worked on and with go for a decade, including reviewing all sorts of Go we write at Google, but I just don’t get this criticism. It’s just not something I see in the code bases I work on. I admit that I may be working on different kinds of programs to what some others do, and I don’t want to deny anyone their legitimate experiences, but my personal experience has been that this is rarely a thing, if ever.
The lack of generics just severely restricts the expressiveness of the type system.
I think the best way to work around this is use of code generation, which is a great tool for internal, controlled environments, but very cumbersome for open source libraries.
It was useful enough that someone made a standalone package: https://github.com/mmatczuk/go_generics
They even gave a FOSDEM talk about this, with a very interesting title.
https://fosdem.org/2019/schedule/event/kubernetesclusterfuck...
I almost never see code that application developers write using it, and usually if someone is writing that code, it's wrong, and I help them figure out how to avoid it.
Usually you can avoid it by using closures or interfaces.
Ubiquitous where? It's the equivalent of `Any` in Scala (and other similar strongly typed languages with unified type hierarchy) and should be strongly discouraged in production code.
Most logging libraries I've seen follow the pattern from fmt.Println etc to accept arbitrary `interface{}` values to be logged instead of actually using some Stringer or Formatter interface in their actual method signatures.
Getting concurrency _with_ parallelism right is a lot easier in rust than it is in go. You need to write thread safe code in both of them, but you get zero support from your compiler in go.
In Go, as long as you are not sharing data, you don't really have to care about whether something is concurrent or parallel, the runtime takes care of it for you. You just use goroutines, IO transparently happens on a dedicated threadpool, CPU heavy tasks are spread out over the available cores.
As long as you restrict sharing to sending owned data over channels, you are mostly in the clear. Of course sometimes performance concerns etc mandate more traditional tools (atomics, mutexes, etc) and there the compiler won't help you anymore, but I'd argue that is the exception in Go rather than the norm.
You can often solve problems with a actor-system inspired design. (learning Erlang/Elixir will probably make you a better Go developer!)
In Rust, you still have to care a lot about what you do. Things like tokio::fs can handle file system IO for you, but you need to ensure that code you are calling does no synchronous IO. Also, CPU heavy workloads should be offloaded to a dedicated CPU thread pool, since the tokio runtime is not intended for juggling CPU intensive tasks in addition to the async IO. This is fine for Rust, a language that wants to give you insight and control, but it is still much more demanding of the developer.
In Rust you can do that too! Just use threads. That's what Go does: it just uses threads.
Async I/O is there if you need better performance than what Rust—or Go—can provide. If you don't need that performance, then don't use async I/O.
As you know, Rust doesn't currently have stable support for a similarly high-level interface to async IO. See e.g. the following reddit thread from 3 months ago:
https://www.reddit.com/r/rust/comments/ausuld/what_is_the_cu...
Sure, modern Linux handles 10k threads just fine, but where Go shines are middle-level network services or microservices where you can easily have 10+ additional tasks for each incoming request. Using real threads quickly becomes unfeasible, and also more cumbersome (passing around threadpools, which threadpool do I use for this task?, do I really want to do those two things in parallel and eat up an extra thread? the cancellation issue, ....).
Also in Rust you'd need to rewrite a lot of code when switching from sync to async, and still need to delegate things to a threadpool since not every dependency will be async enabled.
When the ecosystem stabilizes Rust will be a great choice for the lower stack libraries and servers, where the performance matters. But for many projects the increased complexity, for moderate performance gains, would be a hard sell.
"It is practical to create hundreds of thousands of goroutines in the same address space. If goroutines were just threads, system resources would run out at a much smaller number."
More generally, Go is hardly the first language to implement userspace threads backed by OS threads. Erlang famously does this, for example, and so does GHC: https://en.wikibooks.org/wiki/Haskell/Concurrency
In response to your other comment, the whole point of Goroutines is that you can use them pretty freely without (usually) having to worry about how many there are. Let's say you're writing a server, and that handling one of the requests requires completing 10 tasks, each of which can run in parallel. In Go you can just spawn a Goroutine for each of those tasks. But if you were using real OS threads, then that implementation could easily end up spawning more than a few thousand threads, when the server was under significant load.
> But if you were using real OS threads, then that implementation could easily end up spawning more than a few thousand threads, when the server was under significant load.
There's no inherent problem with spawning a few thousand threads in Linux.
>If you have hundreds of thousands, you probably want true async I/O instead of having stackful threads at all.
This is a completely baseless assertion, and really makes no sense at all. "Async IO" in itself doesn’t even offer a means of exploiting multiple CPU cores. If "async IO" were all that was required, people would be sticking with Node.js for heavy loads.
>even 2kB of stack per thread is 195MB just from thread stacks.
You say this as if 195MB were a large amount of memory for a server to be using! Obviously, if the memory overhead of running 100,000 Goroutines is only 195MB, then memory limitations are not going to prevent you from running 100,000 Goroutines.
> "Async IO" in itself doesn’t even offer a means of exploiting multiple CPU cores.
Sure it does. Multithreaded epoll is the standard solution for such things; that's how nginx is multithreaded, for example. Node isn't multithreaded, but that's a Node problem, not an async I/O problem.
> then memory limitations are not going to prevent you from running 100,000 Goroutines.
100,000 Linux kernel threads with 8kB kernel stack + 2kB user stack is only 1GB, which is likewise tractable.
And that 2kB assumes that the goroutine stacks remain small, which is not always the case. They can and will grow based on the dynamic behavior of the program.
The rest of your comment is just a repeated insistence that "Go doesn't scale" without a shred of evidence in support. You really seem to have some kind of beef with Go that's clouding your judgment in this area.
>Multithreaded epoll is the standard solution for such things
Multithreaded epoll is exactly what Go provides. Rather than having to manage thread pools yourself, the Go runtime does it for you by distributing logical threads of execution over OS threads.
>We usually talk about high scalability as C10K; C100K is an extreme case.
You seem to be assuming one Goroutine per connection. As I said before, a more plausible scenario for 100,000 goroutines is C10K with 10 goroutines per connection.
The Rust compiler won't let you do that.
Edit: And to be clear, I'm not a Go-hater! In fact, Go comes out-of-the-box with a very nice, high-performance concurrent GC that makes it highly appropriate for some scenarios where Rust would not be a good fit at all.
I'd say Go is opinionatedly inelegant. It is minimal, I'll give you that, but has several non-orthogonal design decisions, smells, etc.
[1] - https://www.teamten.com/lawrence/writings/why-i-dont-like-go...
How is "being opinionated" and "making clear promises and delivering on them" equivalent?
I would not want to use Go for any codebase that's expected to last more than a couple of years. I'd put a lot more faith in languages that were able to adapt elegantly to the challenges of concurrency at the library level, without requiring language changes - that suggests those languages will be able to adapt similarly to the next revolution. And my impression is that languages that started with a solid theoretical underpinning, and/or a small core language with as much as possible pushed out into libraries, handled the transition a lot better than languages with the more ad-hoc, practical-problem-first approach to design that Go has.
> Go has been carefully designed to solve the problems of today.
I'd even argue it was "carefully designed" to be in the 70's. Other than "goroutines", the language is stuck in that decade. No generics, no expression based syntax, exposing system-dependent "int" type and encouraging its use, badly designed interfaces that can't be used for tagging, badly designed time package, the list is too long to mention here.
It still runs today. I have Haskell and Python programs that are somewhat smaller from around the same time that no longer run.
More importantly, I can go into my source code and I can still understand it. I can't say the same for the Haskell and Python programs.
I wonder which is more maintainable in the long term.
You can find old C programs that compile and run fine now, so I don't really see the point of your argument.
I say this as one who has basically mastered Haskell, so it's not like I'm not aware of all these things: Fans of all these complicated abstractions really need to take some time and grapple with that fact. It will improve your programming in the more complicated languages as well.
No, seriously. Stop for a moment and think about that. Consider the advanced features as a scientific prediction: "If you do not use these features, you can't have a good program come out the other end." This prediction is falsified by the concrete existence of a lot of good Go programs. This matters. It is something that should be grappled with intellectually, if you are a language fan.
It will bring you to a deeper understanding of all the fancy features. By a deeper understanding, I mean that quite directly, with no attempt to be subtly saying something like "Oh, you'll find they're all useless in the end." That would not be true. They are not even remotely useless. They are incredibly useful. I would even go so far as to say that the development of these complicated sorts of features is legitimately among some of the most simultaneously interesting and useful intellectual work being done in our time, and greatly contributing to our ability to hold our software world together. It's just that the shape of that usefulness is more complicated than a lot of people realize, and a deeper understanding of that complication is very useful for developers.
One of the things that I think really comes out of it is a yet deeper understanding of matching tools to problems. I use a lot of Go, because I write a lot of things that are at a size and complexity where those other language features just aren't that helpful. Yet, there are tasks I could be assigned where I wouldn't dream of using Go, because I know I would need all that fancy stuff to hold the program together. I can easily imagine ways in which my career trajectory would change in the next couple of years and I'd go primarily Rust; I can equally easily imagine ways in which I will stay primarily Go. I can imagine ways in which I'd need to go with some other language. I think a lot of programmers who find these fancy features and fall in love with them end up overestimating the scope of the set of programs where they can be helpful. To give a degenerate example, my six line shell script does not need to be written in the Rust type system. It is not only not helpful, it would be actively harmful. The scope of programs that don't really need those features and where they quite easily become actively harmful is larger than a lot of people think. Especially if you consider them in the context of a heterogeneous team of developers who may not all be in the 5+ years of experience range, or when you may not be able to guarantee that the project is going to stay in those hands.
I'd claim that programs built without those features have a ceiling on how complex a problem they can solve before maintainability collapses under its own weight. Go is popular for microservices, which makes a certain amount of sense (if you believe that microservices are an effective way of doing systems architecture - I don't, but I can see why people would). I've yet to see a Go system that I found genuinely impressive/innovative in terms of the functionality it provided.
> To give a degenerate example, my six line shell script does not need to be written in the Rust type system. It is not only not helpful, it would be actively harmful. The scope of programs that don't really need those features and where they quite easily become actively harmful is larger than a lot of people think.
Disagree. In my experience a significant proportion of production incidents happen because a six line shell script gets modified incorrectly, or does not handle an unexpected condition properly.
I've seen plenty of ineffective solving of business problems in e.g. Haskell, which I think boils down to a failure to appreciate how low the reliability requirements actually are in most businesses (exacerbated because many organisations are dishonest with themselves about such things, even in internal communications). I do think that someone who knows the advanced features but is willing to write "YOLO Haskell" (or similar language) will be more effective at solving business problems and creating value than someone using Go or similar (and at this point I've built my career on the effectiveness of fancier languages, even for 6-line-script-like tasks).
"My" six-line shell script isn't on production. It's something I wrote to rearrange my mp3 collection, or whatever. Then I deleted it.
My personal threshold for leaving shell is quite low. We may make fun of Perl, but it's really a lot, lot better than shell for any serious task. It's even lower for "production" tasks.
But whipping out the Rust because I want a shell script to increase the brightness of my screen through the /proc file system is massive overkill. I can write the thing in the amount of time it takes you to compile the Rust, and I'm not particularly trying to make fun of Rust here... it's just massive overkill.
Which is fine unless it deletes your mp3s, or dumps them all in the same folder, or something. A script inherently has the potential to do a lot of damage, because it can take a lot of actions very quickly. So it's worth being safe even if that means being a little slower.
> But whipping out the Rust because I want a shell script to increase the brightness of my screen through the /proc file system is massive overkill. I can write the thing in the amount of time it takes you to compile the Rust, and I'm not particularly trying to make fun of Rust here...
If your point is that a REPL or shell is a worthwhile feature then I agree with that. But you can have that in a safe language too.
I didn't mention anything about fancy features such as the likes of what Haskell and gang bring. I'm referring to much more "basic" things like generics, good error handling, non-nullable by default pointers, and so on. Things that one has come to expect in a modern language (which golang isn't in general).
Having the ability to provide abstractions does not mean must use them to write software, but that they are available when required, and when used correctly, they end up simplifying the overall system architecture. I think the world has moved on from writing abstractions for the sake of writing abstractions, and I agree that it's not the way to go if such code is still being written. But the solution is not to get rid of the ability completely, resulting in a different sort of mess. At least we have tools that make navigating large code bases easier.
I feel it's actually golang people end up reinventing things differently just for the sake of writing things differently. Take the builder pattern for instance. For some reason, golang code I see uses a different, more convoluted pattern of passing an options struct that gets returned in a closure to emulate that. It's a bigger mess to understand, and probably ends up generating more work for the GC since closures keep getting allocated.
No generics suck, unfortunately. I don't care that most things aren't expressions, and tbh, I don't know a lot of people that do. System-depdendent int is not a problem for me, but I recognize that it is a problem in some fields. I like how interfaces are designed and used in the language, so I don't share your opinion there. For the time package, the only thing that's badly designed is the American-centric parsing system. Everything else is, for me, ahead of the various package I've used in other languages.
This is why i'm sad that Rust has baked async/await into the language. Somewhere in the RFCs, it is stated that "async/await is now known to be the right way to do concurrency" or something like that, but to me it is clear that it is at least a decade too soon to say that.
Afaik only Go and Erlang currently meet (at least my) criteria. No JVM solution currently does.
I personally dislike Go, precisely for many of the reasons why those "people who are sick of programming languages" would like it!
I'm in that age of folks that did pascal in high school, C and Scheme in college, C++ and python in grad school while futzing around with ocaml and common lisp and sather and other fun things. Programming languages are fun.
But, oh, I have so much other stuff to pay attention to. I write C++ a few days a week, which is barely enough to keep move semantics in my head. I also write SQL and analysis docs and internal DSLs and configuration files and python.
At this point, C++ is barely earning its keep, in terms of mental complexity for reward. Go feels a lot nicer, since I can just get in, do my thing, get out, and it'll at least work and give me a compile error if one of the APIs change when I don't look at it for a year.
I'm not about to jump into rust.
So, at least for me, go hits a pretty good sweet spot.
[Edit: I'm not trying to point fingers at rust in particular. It looks fun. If I weren't still working on getting my hobbyist-Haskell better, I'd probably be playing with it. But that would be at home, for fun, not where I have to have the thing in my head for production issues.
It reminds me a bit of the old statement about perl, that it was a fantastic language if you used it every day, but not if you don't. And I don't get to use these things every day any more.]
I like Go for some things, but when using it I miss many things from Java:
* I can connect to the running JVM and change log levels, and other parameters live.
* I can debug a Java file that has compilation errors in it and fix them as I go in some cases. (In Go you can't even compile a file with an unused import)
* Java libraries and frameworks are mature. Some have been around for decades with all the testing and refinement that implies.
For some applications, like a REST server connecting to a database, why would I use Go and use all it's brand new, limited libraries when I could use dropwizard instead and get an easy to deploy JAR file?
Sure Java has some esoteric features, but you don't have to use them.
If you can't keep your team from being clever in Java, I doubt you'll be able to do so just because they use Go.
I've seen many Go libraries attempt to use introspection and backtick 'annotations' to very confusing and buggy effect.
Note that just because the recent tutorials out there mention Spring Boot, doesn't mean you can't wire together a Spring app either through config files or programatically.
What it is though is something that solves problems and has a great ecosystem, instead of reinventing the wheel.
From what I understand there is no Rust being used internally at Google (apart from perhaps individual engineers tinkering with it just for fun).
There is no support for it from the build tools, so anything more than just messing about on your own machine won't work at Google: no libraries, no way to compile it, no way to test it, no way to deploy it, and no way to monitor it.
I would be surprised if there was widespread usage of it, though. Rust chose a whole different set of tradeoffs than Go.
At work, I've seen Go used primarily in projects that would have previously been written in Python or Ruby.
(I'm a newbie with Go so I've probably missed something in the problem you're describing).
The fact that you need to create your own threads pool to run those koroutine, really it's inferior to the Go runtime that does everything for you ( multiplexing goroutines onto os threads, creating new thread if blocking ect .. ), in Go it just works and it makes sense, in Kotlin you need to do many many things manually and it's much more complex for no apparent reasons.
You can just use a shared executor with a growing pool if you don't care.
This makes a lot of sense. I've seen and worked on code at a large US tech company that is mainly reliant on golang for its services, and let me tell you, it's hideous. It's very verbose, and they end up reinventing everything you have in Java land anyway in a much more verbose and error prone way. You also end up losing on the tremendous monitoring and management capability of the JVM, as well as the extremely mature and diverse ecosystem, with integrations into basically anything you can think of. You want to integrate into an "enterprise" system or infrastructure? ew who'd want to do that? we use golang because it's hip, who cares about about all that money making business?
The funny thing is, there's still no getting away from Java/JVM, and they have a good number of services running on the JVM (particularly security related, and data processing pipeline services).
While the language is "simple" (i.e. underpowered) enough that you can read it, it immediately translates into verbosity and complexity in the code base, with things like helper functions that are basically "map/filter/etc" calls in any sane language taking up 8-10 lines of code scattered everywhere, as opposed to them taking a single line of code as they're supposed to. Not to mention things like JSON handling, validation (non-existent way to do them in a non-error-prone way, unlike annotations or attributes in JVM/.NET), no way of doing compile time DI (unlike e.g. Dagger or micronaut on the JVM), the list goes on.
Java the language is improving with things like switch expressions, records, and value types being added. The JVM itself is getting fibers, and better native integration for high performance work. GraalVM enables compilation to native binaries if needed.
You also have languages like Kotlin to use on the JVM if you don't fancy Java itself.
The only reason golang is being pushed around is hype. It's so underpowered that using it for any moderately complex domain becomes a mess. Even generics if they decide to add them won't fix that.
This is not important in many use cases, but if you are planning to run a microservices architecture, this can help a lot with horizontal scaling costs.
This is a completely blanket statement. Not only are there very large open source projects written in go which your company more than likely already uses (kubernetes, docker, prometheus, influxdb to name a few) there are also case studies of very respectable performance in go:
1 million requests/min http://marcio.io/2015/07/handling-1-million-requests-per-min...
1 million websockets in go: https://www.freecodecamp.org/news/million-websockets-and-go-...
event loops in go: (surpasses redis in their benchmarks) https://github.com/tidwall/evio
I see golang more as a progression for JS or Python developers than Java or JVM based languages, as the concurrency model in golang is closer to to pythons + javascript async. The argument that Java, Kotlin, Erlang developers are less likely to move to Go is probably a valid one, but it is completely baseless to say that "go has no place in new code".
The concurrency model in golang with channels makes more sense to someone more familiar with python/javascript who hasn't been exposed to threading or other concurrency models. If you can understand basic coroutines/channels you get the benefits of golangs concurrency (and even parallelism) without much effort, or code complexity, with an order of magnitude more performance that python or javascript for the same feature set.
The profiling tools in golang are also first class (pprof, trace, ect) With pprof, its easy to see even traces of different coroutines, heap allocations, cumulative allocations, these are even safe to sample on production running services and this is provided by the standard library. There are even built in tools to profile GC. The argument that tooling and monitoring doesn't exist in golang is simply not true.
Have you gotten this impression from actually using Rust, or merely from people who themselves have not used Rust? It deliberately omits a ton of ideas from other programming languages (exceptions, classes, data inheritance, monads, and more), and the features it does have are intended to compose well and without a huge amount of mental burden.
Rust forces you to be explicit about nearly everything, Scala is all about allowing you to hide the details in lots of different ways.
That's absolute gold, and I've seen a very accurate real life representation of that in recent times. Uncanny how many of those wheels we keep re-inventing.
That's not really a "pattern for achieving safe concurrency". At one point, your concurrent processes need to communicate somehow or they're parallel more than concurrent.
Getting away from the JVM is one of the reason FOR using Go rather than Kotlin/Scala/Java etc.
$ curl -s 'https://news.ycombinator.com/item?id=20211301' | egrep -o comhead | wc
99 99 792
Seems to have been pretty productive to me.There is no such thing as perfect language and clearly some languages are designed to be better at some scenarios than some other languages...and visa versa. The problem with Go is that it's designed to be a boring language where it's aims are more around language simplicity and API stability. This makes it very easy to play the "death by a thousand paper cuts" game with Go. If you'd believe what you read on HN, nobody would be using it when actually it is a very popular language.
[1] I'm sure that comment will annoy a great many on here but that's my honest observation.
I've used C (closer to Go than Rust) and I've used C++ (closer to Rust than Go) in previous jobs. My experience is that the C programmers tend to focus on the problem at hand. On C++ teams, people spend a lot of time going to C++ conferences and discussing the idiomatic approach to mundane tasks in their superior language. I imagine we'll see the same thing happen in Go and Rust... different personalities are drawn to the different languages.
I'd rather be going to security, networking and crypto conferences instead of wasting a lot of time on a particular language.
The only thing that doesn't exist is a C conference, but that's because C is the least common denominator.
Pretty sure that there are Go conferences.
CFPs are open until end of June. https://papercall.io/gophercon-au-2019
You can say that about any technical option that isn't garbage. That observation doesn't help us learn about Go, and doesn't let us dismiss all the other arguments.
If he or she wants a technical arguments for why they should or shouldn't use Go (which is a subtly different question but results in a significantly different answer) then there is already a wealth of debate on that topic already so there's no new technical perspectives that I - nor anybody else - could actually add.
Go uses multi-word primitive values, which means assignments are not atomic. Consequently, racing assignment statements can leave objects in weird states that violate type and gc invariants. For example, racing assignments to a value of interface type can give you an object with the wrong dictionary for its data.
The argument for not worrying about this is that this is an unlikely scenario. It's reasonable to think in probabilistic terms when clients are not trying to break the software (eg, in games or office software). However, attackers are not probabilistic: they actively search out vulnerabilities and push on them, so potential vulnerabilities turn into actual vulnerabilities in predictable fashion.
Rust also lets you write memory unsafe multithreaded code, if you want to.
That's a false equivalence. Nobody is criticizing Go for having an `unsafe` package. The criticism is that your Go code can be undefined without using the `unsafe` package. In Rust, you need to use `unsafe` in order to write code that has data races. Otherwise, data races are statically prevented in safe code, unlike in Go.
The situation with Go is pretty clear. Channels are safe. If you start mutating shared state across different goroutines, then you’re in the same situation you’d be in if you were doing the same thing in C++. Anyone who doesn't understand why that's potentially unsafe wouldn't be ready for Rust in any case.
Sigh. That's true of Go too. Both Rust and Go have `unsafe` labels that give access to dangerous things that can cause UB. Both Rust and Go are susceptible to UB-at-a-distance if library code internally uses `unsafe` incorrectly. The difference is that, in Go, you can write code with data races without using any `unsafe` at all. Nothing will yell at you (unless you exercise the race in a test and run the race detector). Rust does not suffer from this flaw.
I've been writing both Rust and Go since before each had their 1.0 release, and I like both languages. This isn't some back-handed comment about how I think Go is bad because of this. I rarely trip over data race related bugs in Go, but they do happen, so they aren't a huge issue in practice. (And your continued mention of channels is a red herring. Go has mutexes and idiomatic code uses mutexes quite frequently. Channels are not always the right thing to use.) Nevertheless, the comments here comparing Rust and Go are a fairly important mischaracterization of how each language treats memory safety, and it's important therefore to correct that.
Yes I know. I'm not sure why you think I'm saying that this is not true of Go.
>The difference is that, in Go, you can write code with data races without using any `unsafe` at all
Again, I know. I addressed this in my previous comment.
Less sighing, more reading!
> Anyone who doesn't understand why that's potentially unsafe wouldn't be ready for Rust in any case.
That's most certainly not true. They might not be ready to write high performance generic data structures from scratch, but application level code in Rust generally has about as much explicit `unsafe` as application level Go code, in my experience. (And writing high performance generic data structures isn't really a thing in Go. Not nearly as much as in Rust anyway.)
I'm confused by the second paragraph of your response. My point was that the lack of an explicit 'unsafe' block in Go isn’t that much of an issue, as pretty much everyone knows that Go does not make any memory safety guarantee when accessing shared state from different Goroutines. Anyone who doesn’t know this isn’t ready to get their head around writing multithreaded code in Rust. Rust’s features for doing this are cool, but they require a pretty good understanding of Rust’s complex type and lifetime system.
> Anyone who doesn’t know this isn’t ready to get their head around writing multithreaded code in Rust.
Well, if they aren't using `unsafe` in the process (or, to be fair, relying on buggy library code that uses `unsafe` inappropriately), they're not going to introduce memory unsafety via data races - no matter how "ready" they are. You just can't say the same about C/C++. Or even Go.
I think the point here is that if the word "unsafe" exists as a language construct, someone is not wrong in assuming anything without that mark is safe. Otherwise, why isn't there a "safe" language construct?
LFeO even ;)
This is a confusing statement, to me. This implies that go has a place in old code. Or are you saying that Go should not be used at all, because any future use is "new code"?
Does learning Elixir do the same?
Some teams switch to Go because they are fed up of writing Google Java instead of real Java.
However, when they were exposed to Rust, a lot of them converted to Rust, and now write Rust for Google on internal apps that need performance but safety, especially during concurrency. Others were exposed to Kotlin (especially due to the Android guys), and are championing that as a replacement for Google-flavored Java inside the company, since the JVM is also very sane when it comes to large scale safety during concurrency.
So yeah, if I was writing brand new highly concurrent coroutines-like code, I'd skip Go and Goroutines, and go straight to Kotlin and Akka (not written in Kotlin, but very natural to use with Kotlin), since I end up with the power of the JVM, the elegance of Kotlin's syntax, and the Erlang Actors brought by Akka.
And for those who have never used Erlang, learn it once, write something highly concurrent in it, and you'll learn to distaste almost every other language's faulty concurrency idioms almost immediately. Without the Erlang actors model, you're not going to be writing fault tolerant concurrent systems; just like "all languages converge until they become LISP", all concurrency systems converge until they become an Erlang actor model.