Ergonomic errors in Rust: write fast, debug with ease, handle precisely
Errors show up in three distinct contexts: when you’re writing code, when you’re debugging code, and at runtime when the program needs to handle recoverable errors. And errors are consumed by two distinct consumers with different needs: the developer debugging an application, and the caller making error handling decisions at runtime.
In this post, we’ll explore how stackerror is designed to make working in all three contexts easy while providing rich debugging context for developers and structured codes for runtime error-handling.
In Rust, different tools tend to optimize for some but not all three of these contexts: bare Result
These tools can also conflate the two consumers of errors: error variants often pack data for both debugging and for error handling. But deciding how to handle a recoverable error usually doesn’t require fully structured payloads, just an error code. This pattern of exposing an error code for error handling is used widely across Rust’s ecosystem: std::io::Error exposes an ErrorKind , serde_json::Error has a Category , and HTTP clients surface StatusCode .
Using stackerror you can attach human-readable messages right where failures occur with minimal boilerplate; your errors stack to provide rich context for debugging; you get structured error codes for control flow (HTTP status codes, std::io::ErrorKind , or your own); and your library can expose a single opaque error type that implements std::error::Error .
To make things concrete, this post builds up a small working covering IO, HTTP handling, and simple retries. Stick around (or scroll down) to the end to see it.
Rough edges
Rust has several mature error libraries, each with clear strengths and a few rough edges depending on your needs. Here’s a quick tour of a common error handling journey and the pain points you might experience along the way.
Starting simple with static strings
... continue reading