I 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.
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.