Avoiding heap allocations in (some) async trait methods

I just tried to get rid of some of my #[async_trait] annotations using the last described method, and I feel like giving up already :tired_face:. It generally seems to work, but I even have to include all generic types as arguments to the associated type, e.g.:

pub trait Dumpable: Sized {
    type DumpRet<'a, 'b, W>: Future<Output = io::Result<()>>;
    fn dump<'a, 'b, W>(&'a self, writer: &'b mut W) -> Self::DumpRet<'a, 'b, W>
    where
        W: AsyncWrite + Unpin + Send;

I did encounter one example where I needed to declare a top-level `type Foo<…> = impl …, which happened when I wanted to provide a default implementation for a trait method.

No other error(s) here (yet), but I didn't test it very extensively.

I don't see why Send bounds are an issue. At least I can simply add + Send to the bounds of the associated type in the trait and in each implementation. Maybe it gets messy when the Future shall be Send if some of the arguments are Send. Maybe the issue with trait objects is the biggest problem. Not sure if I really understand all of the problems mentioned in that post though (despite having read it a couple of times). I still feel like a beginner, even if all this is pretty complex stuff.

Bottom line is: The syntax alone is already discouraging me enough to stop me from manually using associated types to avoid the heap allocations. I'll switch back to use #[async_trait] and deal with the runtime penalty (it won't really matter for the project I'm working on). I feel like using associated types would make my code much less readable, and [#real_async_trait] seems to be unusable in practice yet (which has been warned about in its documentation).

Nonetheless, this journey has taught me a lot about lifetimes, associated types, and the impl keyword.

Perhaps I might use the associated type approach in rare cases to get rid of heap allocations in some async trait methods, i.e. where the syntax overkill is worth the efficiency gain.

1 Like