96. Result::inspect_err — Log Errors Without Breaking the ? Chain
You want to log an error but still bubble it up with ?. The usual trick is .map_err with a closure that sneaks in a eprintln! and returns the error unchanged. Result::inspect_err does that for you — and reads like what you meant.
The problem: logging mid-chain
You’re happily propagating errors with ?, but somewhere in the pipeline you want to peek at the failure — log it, bump a metric, attach context — without otherwise touching the value:
| |
Adding a println! means breaking the chain, introducing a match, or writing a no-op map_err:
| |
That closure always has to end with e. Miss it and you’re silently changing the error type — or worse, swallowing it.
The fix: inspect_err
Stabilized in Rust 1.76, Result::inspect_err runs a closure on the error by reference and passes the Result through untouched:
| |
The closure takes &E, so there’s nothing to return and nothing to get wrong. The value flows straight through to ?.
The Ok side too
There’s a mirror image for the happy path: Result::inspect. Same idea — &T in, nothing out, value preserved:
| |
Both methods exist on Option too (Option::inspect) — handy when you want to trace a Some without consuming it.
Why it’s nicer than map_err
map_err is for transforming errors. inspect_err is for observing them. Using the right tool means:
- No accidental error swallowing — the closure can’t return the wrong type.
- The intent is obvious at a glance: this is a side effect, not a transformation.
- It composes cleanly with
?,and_then, and the rest of theResulttoolbox.
| |
Reach for inspect_err any time you’d otherwise write map_err(|e| { log(&e); e }) — you’ll have one less footgun and one less line.