Error Stacking in Rust
Rust error stacking & context
- Core goal: errors should tell humans what went wrong in domain terms, not just where. Stacked/ layered errors with added context at each level are preferred.
- Example style:
decode_msg(...).context(DecodeMessage)?to wrap low‑level errors; some functions (verify_msg) propagate directly. - One strategy: many small, specific error types (even per function) to preserve context without lints; the type system then forces explicit decisions.
thiserror, SNAFU, error-stack, anyhow
- thiserror:
- Auto‑conversion via
#[from]seen by several as an antipattern; it encourages “just bubble up” without context. - You can have multiple variants for the same source error if you don’t use
#[from]and instead map manually, adding fields like paths, operation names, etc.
- Auto‑conversion via
- SNAFU:
- Designed to make contextual error variants the default.
- Provides attributes for transparent wrapping and for “implicit” fields (location, backtrace, or custom data like timestamps).
- Works nicely with things like
SpanTraceand avoids closures/clone boilerplate.
- error-stack and similar crates wrap errors in a
Report, stacking context plus optional backtraces/spantraces and attachments, with emphasis on good output formatting. - anyhow + small macros (e.g.,
h!()inserting file/line) are used as lightweight “just work” solutions.
Backtraces, debug info, and performance
- Debate over whether enabling backtraces meaningfully requires full debug info.
- Some argue only unwind info (
.eh_frame) and symbol tables are needed; full debuginfo is large but can be split out and resolved offline. - Others note Rust’s current backtrace API resolves symbols at runtime, offers limited raw-address access, and debug info can be huge and fragile on some platforms.
Result vs exceptions, checked exceptions, and panics
- Many note Result +
?resembles checked exceptions; some see near‑equivalence, others stress key differences:- Explicit appearance in types and callsites vs hidden control flow.
- Better fit with functional combinators (
map,map_err) and pattern matching.
- Some argue default propagation + automatic stack traces is “just exceptions”; others insist the value is being forced to choose at each boundary.
- Panics are framed as Rust’s unchecked exceptions, acceptable for programmer bugs or invariant violations, while
Resultis for expected domain failures. - There’s extensive disagreement over how much stack trace info belongs in
Resulterrors vs panics, and over the real merits of Java‑style checked exceptions.
Developer ergonomics & ecosystem
- Several comments criticize Rust’s error‑handling ergonomics and ecosystem fragmentation; others respond that patterns and libraries are still maturing.
- A recurring theme: tension between rich, contextual, “semantic stack traces” and ease of use / boilerplate.