Async is bread and butter of JS and you rarely have any larger bits of sync code, so you learn to deal with this.
And then being able to just freely pass bits of data accessing code (closures) around is such wonderful feeling that you'll miss it everywhere else you go.
I recently had a little excursion into Python and had to invent a very nasty hack to concisely keep short bits of code accessible through some constants.
Even old frontend JS was async in that events could be triggered by the user at any time, and in any order, and xhr and image loads requests were async as well.
There's the `worker_threads` (never used it though) which is like web workers in a browsers, but those work by posting messages to eachother.
This is my major reason for using Rust. It's far better to beat your head against a wall when you're writing than when you're debugging. In both c++ and c# it's possible to write subtly wrong code that is basically undebuggable. Often these are intermittent things that show up once every million or more runs. There's no amount of time that will satisfy you after you've run into this, even after you've fixed the problem. It's like having a stalker, you never know if they've stopped stalking you. You can lock your doors, you can buy an alarm system (eg Valgrind suite). But you won't know whether you've really solved the issue.
Of course it's possible there will be other undebuggable issues with Rust, I'm going more by reputation and general impression in blogspace rather than a thorough study. But I thought it was worth a try, and I've been positively impressed thus far.
Experience tells us that humans can't successfully reason about non-trivial concurrent programs unless they exhibit Sequential Consistency. In Rust you're promised this is what you get. Maybe what you wrote is stupid and wrong, but it has Sequential Consistency. "Oh," you exclaim during debugging, "A might happened before B and then we're in a pretty pickle" - you found the bug, now you just need to fix it. This promise is delivered by never allowing code to have references to things some other code might change, thus eliminating data races.
In most other languages that offer concurrency this promise only applies if you wrote a program with no data races and the responsibility to ensure that is with you, so you can accidentally write programs that don't have Sequential Consistency and thus... "Wait, so, A happened before B and B happened before A? Huh? I don't even understand the bug".
Now I'm going to write an amusing aside. One way you could get into this trouble is if your hardware allows arbitrary foreign memory writes as "IPC". The BBC microcomputer allowed this over the network! You could send a bunch of bytes over Econet (a 1980s network from Acorn Computers available for the BBC, Electron and Archimedes computers), addressed to another BBC micro on the network, asking they be written to a RAM buffer, and the remote hardware would do so. We used this to run a Multi-user Dungeon at school in about 1989 or so. A "server" ran the actual MUD software, and individual users signed in from a computer on the network around the school to play, when they typed a command their command buffer was transmitted over Econet, and then the server wrote remotely to their display RAM to show the result on their screen.
Fortunately your computer is not a BBC Microcomputer, and foreign processes do not (on the whole) get to scribble on your program's memory. So this should not be a problem unless you specifically make this unsafe decision in your Rust program.
However almost every time data races and Rust come together in the same sentence it is as if it would prevent all use cases.
The Rustonomicon is not warning you about scary hidden problems in safe Rust, it's warning you about scary problems you need to care about when writing unsafe Rust, so that your unsafe Rust has appropriate safety rails before anybody else touches it.
// Safety: Can't touch this while anybody else might write
This reminds me of the mutex thing. Look at C++ std::mutex. You could implement exactly that in Rust. But, that's not what std::sync::Mutex is at all. Because if you implemented it in Rust, C++ std::mutex is either useless or unsafe and clearly we'd prefer neither.
But do you have some examples of Rust shared memory IPC that you believe are unsafe? It might be instructive to either show why they're actually safe after all or, alternatively, go add the unsafety explanations and work out what a safe wrapper would look like.
The crate public API surface is safe after all, and unless the user has experience in distributed systems, the answer won't come right away.
Rust's standard library takes this very seriously. In many languages if I try to sort() things which refuse to abide by common sense rules like "Having a consistent sort order" the algorithm used may blow up arbitrarily. But Rust's sort() is robust against that. You may create an infinite loop (legal in Rust, causes Undefined Behaviour in C++) and the result of sorting things without a meaningful ordering is unlikely to be helpful if it does finish, but it's guaranteed to be safe, you won't get Undefined Behaviour.
You would have a point if the standard library was batteries included.
When a language sells safety it has to go all in.
Of course just because some Rust is unsafe does not mean it's wrong, it just means that you're relying on it being correct as you have to with all code in unsafe languages.
Hence why doing blank statements like Rust prevents data races, without the context when that is actually 100% true, does no favours to the language advocacy.
I think what you're imagining is just "What if people use Rust to write bad SQL queries?" which again, not a data race. Stupid perhaps, unlikely to give them the results they expected, but not a data race.
While a relevant progress versus what other systems languages are capable of, it still leaves too much out of the table, that tends to be ignored when discussing data consistency safety.
Stuff that usually requires formal methods or TLA+ approaches to guarantee everything goes as smooth as possible.
This makes debugging easier, in the important sense that humans don't seem to be equipped to debug non-trivial programs at all unless they exhibit Sequential Consistency. It's easy enough to write a program for modern computers which doesn't have Sequential Consistency, but it hurts your head too much to debug it.
With your C++ hat on, this might seem like a distinction that doesn't make a difference, lack of Data Race Freedom in a C++ program results in Undefined Behaviour, but so does a buffer overrun, null pointer dereference, signed overflow, and so many other trivial mistakes. So many that as I understand it an entire C++ sub-committee is trying to enumerate them. Thus for a C++ programmer of course any mistake can cause mysterious impossible-to-debug problems so Data Race Freedom doesn't seem important.
Try your Java hat. In Java data races can happen but they don't cause Undefined Behaviour. Write a Java program with a data race. It's hard to reason about what it's doing! It can seem as though some variables take on inexplicable values, or program control flow isn't what you wrote. If you introduce such a race into a complex system you should see that it would be impractical to debug it. Most likely you'd just add mitigations and go home. This is loss of Sequential Consistency in its tamest form, and this is what safe Rust promises to avert.
Suppose you've got a local String variable A. You will move A into a data structure which is going to live much longer and then your local function exits.
In C++ when the function exits, A will get destroyed, so when A is moved into the data structure, A needs to be hollowed out so that whatever is left (a String object with no heap storage) can be safely destroyed on function exit.
In Rust the compiler knows you moved A, therefore there is nothing to destroy at the end of the function, no work needed (at runtime).