- Define errors with display messages in dedicated
errors.rsfiles. Avoid inlining error texts outside of those files - Define
Errorenum andResult<T>types per crate, with public visibility - Implement
IntoResponsetrait for top-level error for API crates - Errors appeared in logs and tracing spans/events should also bring an error stack trace
These conventions establish consistent practices for defining and handling errors in Rust crates using the Snafu error library, and error_stack_trace::debug proc macro enabling error stack trace.
- Derive from
Snafuwhen defining error enums - Use proc macro
error_stack_trace::debugto enable error stack traces - Restrict generated Snafu selectors' visibility to crate level:
#[snafu(visibility(pub(crate)))] - Avoid constructing errors manually except in rare edge cases (e.g. boxed, non-Snafu, or external errors). Always document the reason when doing so.
- Use Snafu’s helpers doing implicit error conversions under the hood:
.context(...), also supports chaining.build().fail().into_error()
- Do implicit error conversions when propagate errors with
?operator - These are preferred over
.map_err(...)which is generally considered an anti-pattern due to loss of context and less ergonomic traceability
Define error enum with #[derive(Snafu)] and #[error_stack_trace::debug]:
#[derive(Snafu)]
#[error_stack_trace::debug]
pub enum Error {...}
When nesting a non-Snafu foreign error - rename its field from source to error, and add #[snafu(source)] for Snafu context as shown below:
#[snafu(source)]
error: ObjectStoreError,
When clippy::result_large_err is triggered, you may box the error variant. Put error into Box<> and add From trait implementation using:
#[snafu(source(from(S3tablesError, Box::new)))]
source: Box<S3tablesError>,
Note: With Snafu, most use cases do not require manual boxing or unboxing. Let Snafu manage implicit conversions where possible.
Define transparent error variant to re-use context and display message from the underlying error. No context selectors are generated for transparent errors, as error has no own context. And thus no .context(...) call can be used, but it still supports implicit .into() conversion, when used with ?:
#[snafu(transparent)]
error: SomeError,