Isn't it too low-level? Why not define instead cleanup/recovery through RAII and occasional try/catch for maintaining tricky invariants, and have no need to manually propagate errors?
>`.map_err(Wrapper)?` is way more convenient than catching and throwing a wrapper
If you only wrap exceptions on the API boundary it might be more ergonomic than having to manually propagate errors inside the API implementation and map them anyway.
>you're in the middle of modifying it and currently it doesn't hold its own invariants because you haven't finished restoring those yet
Sometimes it's better to call throwing functions and get needed data before modifying the private data, sometimes it's time to use try/catch. Is it happening often?
>Exceptions make the other error handling patterns too unergonomic
But so does packing valid data and errors together. Imagine you need to call f(h1()) and f(h2()) where h1() and h2() return the same data type for valid data but different types of errors. What do you do?
>the post lacks a concrete example
Yes, having a specific example seems to me to be the best way to discuss design tradeoffs, but you did the second best thing -- you started with declaring your assumptions (exceptions are disguised return values and optimizing ergonomics of passing errors through f(g(x)) is important) with both of which I happen to disagree)