3
3
4
4
# error-chain - Consistent error handling for Rust
5
5
6
- The [ error-chain crate] ( https://crates.io/crates/error-chain )
7
- ([ docs] ( http://brson.github.io/error-chain/index.html ) ) is a new crate
8
- for dealing with Rust error boilerplate. It provides a few unique
9
- features:
6
+ ` error-chain ` is a crate for dealing with Rust error boilerplate. It
7
+ provides a few unique features:
10
8
11
9
* No error is ever discarded. This library primarily makes it easy to
12
10
"chain" errors with the ` chain_err ` method.
13
11
* Introducing new errors is trivial. Simple errors can be introduced
14
12
at the error site with just a string.
15
13
* Errors create and propagate backtraces.
16
14
17
- I think the lack of the above are widespread problems with Rust error
18
- handling, so I'm interested to hear what people think about this
19
- solution. It is inspired by
20
- [ quick-error] ( https://github.com/tailhook/quick-error ) (and in fact
21
- includes a hacked up version of it for internal use) as well as
22
- Cargo's internal error handling techniques. This library is used by
23
- [ rustup] ( https://github.com/rust-lang-nursery/rustup.rs ) for error
24
- handling.
25
-
26
- One note about usage that isn't included in the docs: the
27
- ` chain_error! ` macro recurses deeply, so you'll probably need to use
28
- the little-known ` #![recursion_limit = "1024"] ` macro on crates that
29
- import it.
30
-
31
- For detailed usage information read [ the
32
- docs] ( http://brson.github.io/error-chain/index.html ) ,
33
- which are reproduced in part below.
15
+ [ Documentation] ( http://brson.github.io/error-chain/index.html ) .
34
16
35
17
## Quick start
36
18
@@ -62,13 +44,7 @@ mod errors;
62
44
Add a file for that module called ` errors.rs ` and put this inside:
63
45
64
46
``` rust
65
- error_chain! {
66
- links { }
67
-
68
- foreign_links { }
69
-
70
- errors { }
71
- }
47
+ error_chain! { }
72
48
```
73
49
74
50
That's the setup. Now when writing modules for your crate,
@@ -90,205 +66,6 @@ fn do_error_prone_work() -> Result<()> {
90
66
}
91
67
```
92
68
93
- ## Declaring error types
94
-
95
- Generally, you define one family of error types per crate, though it's
96
- also perfectly fine to define error types on a finer-grained basis,
97
- such as per module.
98
-
99
- Assuming you are using crate-level error types, typically you will
100
- define an ` errors ` module and inside it call ` error_chain! ` :
101
-
102
- ``` rust
103
- // Define the error types and conversions for this crate
104
- error_chain! {
105
- // The type defined for this error. These are the conventional
106
- // and recommended names, but they can be arbitrarily chosen.
107
- // It is also possible to leave this block out entirely, or
108
- // leave it empty, and these names will be used automatically.
109
- types {
110
- Error , ErrorKind , ChainErr , Result ;
111
- }
112
-
113
- // Automatic conversions between this error chain and other
114
- // error chains. In this case, it will e.g. generate an
115
- // `ErrorKind` variant called `Dist` which in turn contains
116
- // the `rustup_dist::ErrorKind`, with conversions from
117
- // `rustup_dist::Error`.
118
- //
119
- // This section can be empty.
120
- links {
121
- rustup_dist :: Error , rustup_dist :: ErrorKind , Dist ;
122
- rustup_utils :: Error , rustup_utils :: ErrorKind , Utils ;
123
- }
124
-
125
- // Automatic conversions between this error chain and other
126
- // error types not defined by the `error_chain!`. These will be
127
- // wrapped in a new error with, in this case, the
128
- // `ErrorKind::Temp` variant. The description and cause will
129
- // forward to the description and cause of the original error.
130
- //
131
- // This section can be empty.
132
- foreign_links {
133
- temp :: Error , Temp ;
134
- }
135
-
136
- // Define additional `ErrorKind` variants. The syntax here is
137
- // the same as `quick_error!`, but the `from()` and `cause()`
138
- // syntax is not supported.
139
- errors {
140
- InvalidToolchainName (t : String ) {
141
- description (" invalid toolchain name" )
142
- display (" invalid toolchain name: '{}'" , t )
143
- }
144
- }
145
- }
146
- ```
147
-
148
- This populates the the module with a number of definitions, the most
149
- important of which are the ` Error ` type and the ` ErrorKind ` type. They
150
- look something like the following:
151
-
152
- ``` rust
153
- use std :: error :: Error as StdError ;
154
- use std :: sync :: Arc ;
155
-
156
- #[derive(Debug )]
157
- pub struct Error (pub ErrorKind ,
158
- pub Option <Box <StdError + Send >>,
159
- pub Arc <error_chain :: Backtrace >);
160
-
161
- impl Error {
162
- pub fn kind (& self ) -> & ErrorKind { ... }
163
- pub fn into_kind (self ) -> ErrorKind { ... }
164
- pub fn iter (& self ) -> error_chain :: ErrorChainIter { ... }
165
- pub fn backtrace (& self ) -> & error_chain :: Backtrace { ... }
166
- }
167
-
168
- impl StdError for Error { ... }
169
- impl Display for Error { ... }
170
-
171
- #[derive(Debug )]
172
- pub enum ErrorKind {
173
- Msg (String ),
174
- Dist (rustup_dist :: ErrorKind ),
175
- Utils (rustup_utils :: ErrorKind ),
176
- Temp ,
177
- InvalidToolchainName (String ),
178
- }
179
- ```
180
-
181
- This is the basic error structure. You can see that ` ErrorKind ` has
182
- been populated in a variety of ways. All ` ErrorKind ` s get a ` Msg `
183
- variant for basic errors. When strings are converted to ` ErrorKind ` s
184
- they become ` ErrorKind::Msg ` . The "links" defined in the macro are
185
- expanded to ` Dist ` and ` Utils ` variants, and the "foreign links" to
186
- the ` Temp ` variant.
187
-
188
- Both types come with a variety of ` From ` conversions as well: ` Error `
189
- can be created from ` ErrorKind ` , from ` &str ` and ` String ` , and from
190
- the "link" and "foreign_link" error types. ` ErrorKind ` can be created
191
- from the corresponding ` ErrorKind ` s of the link types, as wall as from
192
- ` &str ` and ` String ` .
193
-
194
- ` into() ` and ` From::from ` are used heavily to massage types into the
195
- right shape. Which one to use in any specific case depends on the
196
- influence of type inference, but there are some patterns that arise
197
- frequently.
198
-
199
- ## Chaining errors
200
-
201
- This is the focus of the crate's design. To extend the error chain:
202
-
203
- ```
204
- use errors::ChainErr;
205
- try!(do_something().chain_err(|| "something went wrong"));
206
- ```
207
-
208
- ` chain_err ` can be called on any ` Result ` type where the contained
209
- error type implements ` std::error::Error + Send + 'static ` . If the
210
- ` Result ` is an ` Err ` then ` chain_err ` evaluates the closure, which
211
- returns * some type that can be converted to ` ErrorKind ` * , boxes the
212
- original error to store as the cause, then returns a new error
213
- containing the original error.
214
-
215
- The above example turns a string into an error, but you could also write e.g.
216
-
217
- ```
218
- try!(do_something().chain_err(|| ErrorKind::Foo));
219
- ```
220
-
221
- ## Returning new errors
222
-
223
- Introducing new error chains, with a string message:
224
-
225
- ``` rust
226
- fn foo () -> Result <()> {
227
- Err (" foo error!" . into ())
228
- }
229
- ```
230
-
231
- Introducing new error chains, with an ` ErrorKind ` :
232
-
233
- ``` rust
234
- fn foo () -> Result <()> {
235
- Err (ErrorKind :: FooError . into ())
236
- }
237
- ```
238
-
239
- Note that the return type is is the typedef ` Result ` , which is defined
240
- by the macro as `pub type Result<T > = ::std::result::Result<T,
241
- Error>` . Note that in both cases ` .into()` is called to convert a type
242
- into the ` Error ` type: both strings and ` ErrorKind ` have ` From `
243
- conversions to turn them into ` Error ` .
244
-
245
- When the error is emitted inside a ` try! ` macro or behind the ` ? `
246
- operator, then the explicit conversion isn't needed, since the
247
- behavior of ` try! ` will automatically convert ` Err(ErrorKind) ` to
248
- ` Err(Error) ` . So the below is equivalent to the previous:
249
-
250
- ``` rust
251
- fn foo () -> Result <()> {
252
- Ok (try ! (Err (ErrorKind :: FooError )))
253
- }
254
-
255
- fn bar () -> Result <()> {
256
- Ok (try ! (Err (" bogus!" )))
257
- }
258
- ```
259
-
260
- ## Foreign links
261
-
262
- Errors that do not conform to the same conventions as this library can
263
- still be included in the error chain. They are considered "foreign
264
- errors", and are declared using the ` foreign_links ` block of the
265
- ` error_chain! ` macro. ` Error ` s are automatically created from foreign
266
- errors by the ` try! ` macro.
267
-
268
- Foreign links and regular links have one crucial difference: ` From `
269
- conversions for regular links * do not introduce a new error into the
270
- error chain* , while conversions for foreign links * always introduce a
271
- new error into the error chain* . So for the example above all errors
272
- deriving from the ` temp::Error ` type will be presented to the user as
273
- a new ` ErrorKind::Temp ` variant, and the cause will be the original
274
- ` temp::Error ` error. In contrast, when ` rustup_utils::Error ` is
275
- converted to ` Error ` the two ` ErrorKinds ` are converted between each
276
- other to create a new ` Error ` but the old error is discarded; there is
277
- no "cause" created from the original error.
278
-
279
- ## Backtraces
280
-
281
- The earliest non-foreign error to be generated creates a single
282
- backtrace, which is passed through all ` From ` conversions and
283
- ` chain_err ` invocations of compatible types. To read the backtrace
284
- just call the ` backtrace() ` method.
285
-
286
- ## Iteration
287
-
288
- The ` iter ` method returns an iterator over the chain of error
289
- boxes. [ See how rustup uses this during error
290
- reporting] ( https://github.com/rust-lang-nursery/rustup.rs/blob/master/src/rustup-cli/common.rs#L344 ) .
291
-
292
69
## License
293
70
294
71
MIT/Apache-2.0
0 commit comments