In a situation like this:
trait Foo { async fn foo(&self) -> Bar; }
trait Baz: Foo { async fn baz(&self) -> Meow { ...await self.foo() into a Meow ... }
you'll end up with boxes of boxes of boxes. It kinds of makes the feature something for the outermost abstraction layer or you end up with boxes of boxes of boxes.> And of course it’s worth highlighting that most languages box all their futures, all of the time. =)
It's also worth highlighting that most of those languages (1) have a GC that makes boxing a much cheaper operation than in Rust, and (2) those languages aren't advertised as "low-level" languages and that justifies implicit boxing as a trade-off.
---
I feel that async shouldn't really be special but rather just build on other features that stand on its own. GATs and impl Trait in Traits seem reasonable. But being able to do dynamic dispatch on trait methods that return a type that isn't `Sized` seems like a tough problem to solve, and the blog post didn't managed to convince me that implicit boxing is the right approach here. AFAICT, we need either some kind of boxing, or better support for unsized rvalues or similar. I think I would be more comfortable with "sugar" for boxing if the caller of the trait method were in control of where exactly the result is allocated (not necessarily a Box), but that calls for some kind of placement syntax.