3 ms·
Everything you're describing goes back to problems with thread APIs. I agree that threads as implemented are very coarse grained and limited. What I dislike is
by api 2y ago
Everything you're describing goes back to problems with thread APIs. I agree that threads as implemented are very coarse grained and limited. What I dislike is the idea of inflicting an explosion of cognitive load onto the programmer to manually manage things instead of thinking about how to implement a better kind of thread.
The latter is what Go did. I'm not a giant Go fan but not having async is far and away the best thing about the language and makes up for almost all its other faults.
Anything the language makes the programmer think about detracts from the programmer's ability to think about the actual problem they are solving.
BTW Rust is still superior to C++ and I use it, so I am not dissing Rust too badly. The async cancer is found across the entire ecosystem, not just Rust, so my comment was more against async programming in general as opposed to getting threading right. If we could get threading right we would massively simplify all programming everywhere.
Ultimately it boils down to the fact that it's 2024 and we still run everything on 1970s operating systems.
- pornel 2y agoI don't get how can you think Golang does it better. Its async is not easily composable. Anything beyond async 101 becomes a tangled web of DIY goroutines, wait groups, channels, callbacks, special empty signal channels for selects, and the Context objects which need careful management to be shared but not shared too much to cancel and time out right parts at the right time. And then all of that has to be thread-safe, even though golang built-in types aren't. You need to pay sync overhead even for I/O-bound workloads or thread-per-core designs, because you don't control the fat runtime, and some of your green threads may be real.
- pornel 2y agoI suspect that most the hate for Rust's async is from overuse of tokio's multi-threaded spawn(), as if it was the `go` of goroutines. Tokio's mt spawn adds burdensome Send and 'static requirements, and often gives too fine granularity, especially if the tasks still need to return results to their caller. spawn() is often replaceable with join_all and streams. These allow same-thread temporary data, and are runtime-agnostic. The other self-inflicted pain is stubborn avoidance of boxed futures. Every escaping pointer in golang is heap allocated, and nearly everything goes through interfaces. If you allow Box and dyn that often in Rust, you can skip a lot of the type system and lifetime complexity too.