That's amusing.<p>I've been arguing against "async contamination" for a few years now, because if you have both compute-bound and I/O bound work, async makes it harder. There's been enough pushback now to keep tokio from taking over the world. You can still use Rust without async. A year ago, it wasn't clear you'd be able to today.<p>There are situations where async is appropriate. Twitter (pre-Musk) used to ask applicants "A celebrity with a million followers sends out a tweet. How do you get it to all their followers within three seconds?" If you have to keep millions of connections open on your servers for reverse push notifications, you probably need async. Otherwise, probably not.<p>There's a broader problem. Go was built to do web server side stuff, and it's optimized for that use case. Google staff wrote all the important packages, and Google uses those packages internally. So even the obscure cases have been well-exercised. Go also has "goroutines", which, at some cost in overhead, provide the functionality of both full threads and async execution. Goroutines can block or go compute-bound without stalling out the system. Rust async executors cannot. Thus, Rust has the "function coloring" problem, but Go does not.<p>Rust is supposed to be for problems where C++ gets too tangled. Keeping concurrency straight in C++ is hard. Keeping ownership straight, and not getting dangling pointers or bad pointers, is hard in C++, too. Rust does fix those problems. So does Go, mostly. Garbage collection hides a multitude of sins.<p>In Rust, the formal semantics have to be right or the program won't compile. This means time spent getting the plumbing correct, and major revisions can be painful. It also means you need programmers who have some sense of formalism, which probably means a decent CS degree from somewhere. You shouldn't need such people just to make a web site work. It's like requiring electricians to have EE degrees.<p>Some things for which you'd think async might help don't benefit from it. One-many relationships are an example. Something that comes up regularly for me is this pattern in a game-like program.<p>- Discover that asset ABC is needed.<p>- Is it in memory already? No.<p>- In disk cache? No.<p>- Put on fetch queue, which has a priority order, and gets reordered depending on where the player moves to.<p>- Take off fetch queue. Recheck.<p><pre><code> - Is it in memory already? No.
- Is it in disk cache? No.
- Is it currently being fetched for some other need? No.
- Are all the things that need it gone? No.
- Fetch from network.
- Are all the things that need it gone? No.
- Do decoding and processing, which is compute bound.
- Tell all the things that need it that it's now available.
- If there weren't any, log the wasted effort.
</code></pre>
This seems like a good candidate for async code. But async code is not that good at priority queuing, mixed compute bound and I/O bound work, or many-to-many waiting. So it's a bad fit. Async is kind of a one-trick pony - lots of more or less independent I/O bound tasks. That's a big niche, but a niche nevertheless.