I pray for all the code written by people who think they didn’t need to learn about synchronization because they wrote asynchronous code.
And unfortunately I’ve come across and had to fix asynchronous code with race conditions.
You cannot escape learning about synchronization. Writing race-condition-free code is not hard.
What is actually hard is writing fast lock-free routines, but that’s more a parallelism problem that affects both threaded and asynchronous code. And most people will never need to reach that level of code optimization for their work.
Javascript has race conditions too, even with no threads involved.
There are no shortcuts, no silver bullets when it comes to concurrency. Programmers have to learn about multi-threading and the ways it can go wrong because it's a fundamental thing, not a mere feature you can design your way out of.
But you can identify the opposite of the silver bullet, which is shared mutable state, and then hurry away from it as quickly as possibly.
Rust defaulted to avoiding sharing and Haskell defaulted to avoiding mutability.
For application code, I've yet to see a better concurrency story than 'use pure functions where possible, or use STM when you absolutely need shared mutation'.
You can't get data races, which is what Rust prevents. Rust's async doesn't prevent deadlocks or other kinds of races.
At which point they're not really threads, they're more, well, processes.
> This is what Erlang does.
Exactly.
Also, Rust’s ownership model only prevents data races, that’s only the tip of the iceberg of race conditions, and I don’t think that any general model makes it possible to statically determine that any given multithreaded code is safe. Nonetheless, that’s the only way to speed up most kind of code, so possibly the benefits outweigh the cost in many cases.