1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! **Experimental Error Return Tracing for Rust.**
//!
//! The immediate goals of this library are to:
//! 1. provide a minimal-boilerplate error handling story based around error
//! return tracing, and
//! 2. demonstrate the value of error return tracing with the hopes of
//! getting support directly integrated into the Rust compiler.
//!
//! This library is very much in its early days and is highly unstable. Some
//! effort has been made to implement functionality with performance in mind,
//! but, so far, no profiling has been performed. There is undoubtedly room for
//! improvement.
//!
//! ## Error Return Tracing
//!
//! Error return tracing is a novel error handling concept developed by
//! Andrew Kelley for the Zig programming language. Error return traces look a
//! bit like the stack traces displayed by many popular programming languages
//! when an exception goes uncaught. Stack traces provide extremely valuable
//! information for identifying the source of an error, but, unfortunately, they
//! have a considerable performance cost. For this reason, Rust only enables
//! stack traces for panics, and only when the `RUST_BACKTRACE` environment
//! variable is defined.
//!
//! Error return traces provide similar information to stack traces, but at
//! a far smaller performance cost. They achieve this by tracing errors
//! as they bubble up the call stack, rather than by capturing an entire
//! stack trace when an error is first encountered. (For more information on
//! the performance differences between stack traces and error return traces,
//! please see the [section on performance](
//! #performance-stack-traces-vs-error-return-traces), below.)
//!
//! Furthermore, error return traces can even provide *more* useful information
//! than basic stack traces, since they trace where and why an error of one type
//! causes an error of another type. Finally, since the errors are traced
//! through each return point, error return tracing works seamlessly with
//! M:N threading, futures, and async/await.
//!
//! ## Example
//!
//! ```rust,should_panic
//! use ertrace::{ertrace, Ertrace};
//!
//! fn main() -> Result<(), AError> {
//! // Forward any `AError` errors from `a`.
//! a().map_err(|mut e| ertrace!(e =>))
//! }
//!
//! fn a() -> Result<(), AError> {
//! // On any error in `b`, return an `AError`, and trace the cause.
//! b().map_err(|e| ertrace!(e => AError))?;
//! Ok(())
//! }
//!
//! fn b() -> Result<(), BError> {
//! // Forward any `BError` errors from `b_inner`.
//! b_inner().map_err(|mut e| ertrace!(e =>))
//! }
//!
//! fn b_inner() -> Result<(), BError> {
//! if true {
//! // Initialize the traced error struct, `BError1`, and then use the `?`
//! // operator to convert it into the appropriate `BError` enum instance
//! // and return it.
//! Err(ertrace!(BError1))?
//! } else {
//! // Initialize the traced error struct, `BError2`, and then use the `?`
//! // operator to convert it into the appropriate `BError` enum instance
//! // and return it.
//! Err(ertrace!(BError2))?
//! }
//! }
//!
//! ertrace::new_error_types! {
//! // Define new traced error structs `AError`, `BError1`, and `BError2`.
//! pub struct AError(Ertrace);
//! pub struct BError1(Ertrace);
//! pub struct BError2(Ertrace);
//!
//! // Define a new traced error enum `BError`, with variants for
//! // `BError1` and `BError2`.
//! pub enum BError {
//! BError1(BError1),
//! BError2(BError2),
//! }
//! }
//! ```
//!
//! Output:
//!
//! ```skip
//! Error: AError
//! error return trace:
//! 0: BError1 at examples/basics.rs:24:13 in basics
//! 1: => at examples/basics.rs:16:31 in basics
//! 2: AError at examples/basics.rs:10:21 in basics
//! 3: => at examples/basics.rs:5:25 in basics
//! ```
//!
//! ## `no_std` Support
//!
//! Ertrace provides `no_std` support. By default, it depends on the `std` crate,
//! in order to provide additional functionality, but
//! this dependency is gated behind the `std` feature, and can be disabled by
//! specifying `default-features = false` in your Cargo dependencies.
//!
//! Currently, the `alloc` crate is required, but it should be straight-forward to
//! remove even that requirement by specifying a static block of memory in which
//! to store error traces. If you have a need for this,
//! [please open a Github issue](https://github.com/scottjmaddox/ertrace/issues/new).
//!
//! ## Performance: Stack Traces vs. Error Return Traces
//!
//! In order for a stack trace to be displayed when an exception goes uncaught, the
//! entire stack trace must be captured when the exception is created (or when it is
//! thrown/raised). This is a fairly expensive operation since it requires
//! traversing each stack frame and storing (at minimum) a pointer for each function
//! in the call stack, typically in some heap-allocated thread-local storage. The
//! argument usually made is that exceptions should only be thrown in exceptional
//! cases, and so the performance cost of collecting a stack trace will not
//! significantly degrade the overall program performance. In reality, though,
//! errors are quite common, and the cost of stack traces is not negligible.
//!
//! In contrast, the cost of error return tracing starts very small, and scales
//! linearly with the number of times errors are returned. If an error is
//! handled one stack frame above where it is first created, the overhead
//! runtime cost can be as small as a few ALU ops and a single memory write
//! (if you have compiler support... The runtime overhead for this library
//! implementation is a bit higher).
// #![deny(missing_docs)] // TODO: uncomment this
pub use crate*;
pub use crate*;
pub use crate*;
pub use crate*;
pub use crate*;
pub use crate*;