... Until third-party libraries push you to use specific features.
(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.)
I have come to the same opinion about Scala. Stick to the basics (i.e. the Scala Book[0] and doesn't even have to be all of it) and it is a joy to use.
[0]: https://docs.scala-lang.org/scala3/book/introduction.html
Dancing around with lifetimes can be premature optimization. Yes you can write very efficient code that way but if you find yourself spending tons of time fighting the borrow checker you might be overdoing it.
I tend to use Arc<> a lot in async code. It makes things relatively straightforward and easy to reason about. Mixing lifetimes with async is probably the most confusing thing you can possibly do.