Error Stacking in Rust
Rust’s `Result`-based error handling is pushing developers to design richer, “stacked” errors that preserve both domain meaning and call context, instead of blindly propagating failures with `?` or type-erased crates. Commenters compare libraries like thiserror and SNAFU, techniques for automatically capturing locations, backtraces, and span traces, and argue over when to wrap errors with explicit context versus relying on implicit conversions. A recurring theme is the trade-off between ergonomics, binary size, and debuggability, and how far Rust’s model should go toward exception-like behavior while still forcing developers to think carefully about failures.
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.