| 1. Don't use the `async` ecosystem. 2. Prefer dynamic dispatch to monomorphization (i.e., use fewer generics). 3. Don't use proc macros (i.e., don't depend on the `syn` crate, even transitively). Easy to say; hard to put into practice. But that's all there is to it. |
I want to make it /very/ clear that async isn't to blame at all for the pathological build times described here. It's a bug about traits and lifetimes, both very core concepts of Rust that you deal with even if you stay away from async code.
async rust will certainly be more ergonomic once some more improvements land (hopefully later this year), but I don't feel like it deserves all the sighs it's been publicly getting these past few months. (And I /love/ to complain. I've written pieces named "Surviving Rust async interface", "Getting in and out of trouble with Rust futures", "Pin and suffering", etc.)
> Prefer dynamic dispatch to monomorphization (i.e., use fewer generics).
Unless you hit a pathological case as shown in the article, it tends to not be _that_ bad, especially if you enable `-Z share-generics=y` (unstable still, yet enabled by default for debug builds if I remember correctly).
Overall still solid advice - although "use fewer generics" sometimes turns out to be "just turn a big generic type into `Box<dyn Trait>`" (it's not _just_ boxing, that would be `Box<T>`). That's what axum[1] does with all services, and it's never had the compile times issues warp[2] had, for example.
> Don't use proc macros (i.e., don't depend on the `syn` crate, even transitively).
Good news there, I hear there's some progress on the proc-macro bridge (which improves macro expansion performance) AND "wasm proc-macros". I hope this piece of advice will be completely irrelevant in a year (but for now, it's spot-on. using pin-project-lite instead of pin-project is worth it, for example).
[1] https://lib.rs/crates/axum
[2] https://lib.rs/crates/warp