Skip to main content

snafu/guide/examples/
backtrace.rs

1//! Exposing complete backtraces to the location of the error.
2//!
3//! Start by looking at the error type [`Error`].
4
5use crate::{Snafu, Backtrace, ErrorCompat, GenerateImplicitData};
6
7/// Rust 1.65 stabilized the [`std::backtrace::Backtrace`] type, but
8/// there's not yet a stable abstraction for accessing a backtrace
9/// from an arbitrary error value. SNAFU provides a stable-compatible
10/// way of accessing backtraces on a SNAFU-created error type. SNAFU
11/// also supports environments where backtraces are not available,
12/// such as `no_std` projects.
13///
14/// When defining error types which include backtraces, it's
15/// recommended to start with a [`Backtrace`] field on every leaf
16/// error variant (those without a `source`). Backtraces are only
17/// captured on failure.
18///
19/// Certain errors are used for flow control. These don't need a
20/// backtrace as they don't represent actual failures. However,
21/// sometimes an error is *mostly* used for flow control but might
22/// also indicate an error. In those cases, you can use
23/// `Option<Backtrace>` to avoid capturing a backtrace unless an
24/// environment variable is set by the end user to provide additional
25/// debugging.
26///
27/// For variants that do have a source, you need to evaluate if the
28/// source error provides a backtrace of some kind. If it is another
29/// SNAFU error, for example, you can *delegate* retrieval of the
30/// backtrace to the source error. If the source error doesn't provide
31/// its own backtrace, you should capture your own backtrace. This
32/// backtrace will not be as useful as one captured by the source
33/// error, but it's as useful as you can get.
34///
35/// When you wish to display the backtrace of an error, you can use
36/// the [`ErrorCompat::backtrace`] method. It's recommended to always
37/// use this in the fully-qualified form so it will be easy to find
38/// and replace when there's a stable way to access backtraces.
39///
40/// ```
41/// # use snafu::guide::examples::backtrace::*;
42/// use snafu::ErrorCompat;
43///
44/// fn inner_process() -> Result<(), Error> {
45///     // Complicated logic
46///     # UsualCaseSnafu.fail()
47/// }
48///
49/// fn main() {
50///     if let Err(e) = inner_process() {
51///         eprintln!("An error occurred: {}", e);
52///         if let Some(bt) = ErrorCompat::backtrace(&e) {
53///             eprintln!("{:?}", bt);
54///         }
55///     }
56/// }
57/// ```
58#[derive(Debug, Snafu)]
59// This line is only needed to generate documentation; it is not
60// needed in most cases:
61#[snafu(crate_root(crate), visibility(pub))]
62pub enum Error {
63    /// The most common case: leaf errors should always include a
64    /// backtrace field.
65    UsualCase {
66        backtrace: Backtrace,
67    },
68
69    /// When an error is expected to be created frequently but the
70    /// backtrace is rarely needed, you can wrap it in an
71    /// `Option`. See [the instructions][] on how to access the
72    /// backtrace in this case.
73    ///
74    /// [the instructions]: GenerateImplicitData#impl-GenerateImplicitData-for-Option<Backtrace>
75    UsedInTightLoop {
76        backtrace: Option<Backtrace>,
77    },
78
79    /// This error wraps another error that already has a
80    /// backtrace. Instead of capturing our own, we forward the
81    /// request for the backtrace to the inner error. This gives a
82    /// more accurate backtrace.
83    SnafuErrorAsSource {
84        #[snafu(backtrace)]
85        source: ConfigFileError,
86    },
87
88    /// This error wraps another error that does not expose a
89    /// backtrace. We capture our own backtrace to provide something
90    /// useful.
91    SourceErrorDoesNotHaveBacktrace {
92        source: std::io::Error,
93        backtrace: Backtrace,
94    },
95}
96
97/// This is a placeholder example and can be ignored.
98#[derive(Debug, Snafu)]
99#[snafu(crate_root(crate))]
100pub enum ConfigFileError {
101    Dummy { backtrace: Backtrace },
102}