For example, let's say the function frobnicate() writes to a file and might get all sorts of file errors (disk full, no permissions, etc.). It seems like those have to be wrapped or embedded, as the article suggests.
But then you can't use the "?" macro, I think? Because wrapping or embedding requires constructing a new error value from the original one. Every single downstream error coming from places like the standard library has to go through a transformation with map_err() or similar.
You can use ? when you implement From (to get an automatic Into) which which can be as easy as a #[from] annotation with thiserror. You can manually implement From instead of inlining the map_err if you so choose. Then you are only ever using map_err to pass additional contextual information. You usually end up using ? after map_err or directly returning the result.
It may be that you do in fact have to throw your hands up, and propagate _something_ to the caller; in those cases, I find a Box<dyn Error> or similar better maintains the abstraction boundary.
The only thing I disagree with is error wrapping. While I agree that the main error should not expose dependencies, I find it useful to keep a `cause` field that corresponds to the inner error. It's important to trace the origin of an error and have more context about it. By the way, Rust's [Error] trait has a dedicated `cause` method for that!
[Error] https://doc.rust-lang.org/nightly/core/error/trait.Error.htm...