I had a small Rust service written in the futures-combinator era, all and_then, map_err, and the occasional Box<dyn Future> where the types got too clever for me to spell out. It worked. It had worked for years. But every time I opened it I had to reload the whole call chain into my head, because the control flow read backwards and sideways at once, and the error handling was a maze of closures.
async/await has been stable since 1.39, so I'm late to this, but I finally sat down and rewrote it. The mechanical part was almost dull: a chain of .and_then(|x| ...) becomes let x = thing().await?; and suddenly it reads top to bottom like ordinary code.
let conn = pool.get().await?;
let row = conn.query_one(SQL, &[&id]).await?;
let user = User::from_row(row)?;
That's the whole point. The combinator version of those three lines was a nested closure I'd have to draw on paper. The async version is just three statements that happen to be able to yield. The borrow checker pushed back in a couple of spots where a value lived across an .await, which is fair, it was right and I was wrong. Otherwise the rewrite shrank the file by a third and the mental load by considerably more. Should have done it sooner.