4 ms·
I get the async frustration, but keep in mind that there are really only three possible ways of handling this: (1) Async in the core language and do async I/O.
by api 4y ago
I get the async frustration, but keep in mind that there are really only three possible ways of handling this:
(1) Async in the core language and do async I/O.
(2) A fat runtime like Go that implements lightweight concurrency.
(3) Everyone hand-rolls their own implementations of select, kqueue, epoll, etc. loops as well as optimizations like io_uring for every single application.
Rust chose (1) because (2) is off the table as it's a systems language and (3) is more painful and bug-prone than (1).
As for how to implement async: I am having trouble thinking of a significantly better model than Rust and the fact that the Rust community hasn't dramatically improved async shows that a bunch of people who are way smarter than me about languages and type systems are also having a tough time here. Doing async natively in a systems language with virtually no inherent overhead is some seriously difficult stuff.
I personally think the biggest oops with Rust async is the absence of a runtime in the standard library, forcing everyone to pick a third party library and causing fragmentation around which one to use. Tokio seems like the clear winner but having played with both Tokio and Smol I really think the latter is better designed. Tokio does not implement structured concurrency and makes dealing with lifetimes harder. If we used Smol there would be less of a temptation to just throw Arc<> everywhere and less memory leak bugs around forgotten-about tasks.
(For those curious about the latter: Smol JoinHandles abort when dropped while Tokio just forgets about tasks and leaves them running when handles are dropped.)