I had a small networked service written in the old style, the one built out of and_then and map_err chains stitched together with combinators until the whole thing read like a particularly hostile origami diagram. It worked. It also made me wince every time I opened it, because following the control flow meant tracing types through five layers of nested closures.
async/await has been stable in Rust for a while now and I had been putting off the port for no good reason other than inertia. This weekend I finally did it, and I want to record how pleasant it was, because rewrites are usually a slog and this one was the opposite.
The bulk of the work was deletion. A combinator chain that built a future by hand collapsed into linear code that reads top to bottom like ordinary synchronous logic, with .await at the points where you previously needed an and_then. Error handling went from map_err gymnastics to the humble ? operator, which works across await points exactly as you would hope. Whole helper functions that existed only to massage future types out of existence simply evaporated. The borrow checker had opinions about a couple of values held across .await, which is fair, and the fixes were obvious once it pointed them out.
The diff was satisfyingly red. More lines removed than added, the binary behaves identically, and the code is now something I can hand to someone else without an apology. I should have done it months ago. Sometimes the rewrite you have been dreading turns out to be the kind where you mostly press delete.