]> git.proxmox.com Git - rustc.git/blob - src/vendor/failure/book/src/error-errorkind.md
New upstream version 1.28.0~beta.14+dfsg1
[rustc.git] / src / vendor / failure / book / src / error-errorkind.md
1 # An Error and ErrorKind pair
2
3 This pattern is the most robust way to manage errors - and also the most high
4 maintenance. It combines some of the advantages of the [using Error][use-error]
5 pattern and the [custom failure][custom-fail] patterns, while avoiding some of
6 the disadvantages each of those patterns has:
7
8 1. Like `Error`, this is forward compatible with new underlying kinds of
9 errors from your dependencies.
10 2. Like custom failures, this pattern allows you to specify additional information about the error that your dependencies don't give you.
11 3. Like `Error`, it can be easier to convert underlying errors from dependency
12 into this type than for custom failures.
13 4. Like custom failures, users can gain some information about the error
14 without downcasting.
15
16 The pattern is to create two new failure types: an `Error` and an `ErrorKind`,
17 and to leverage [the `Context` type][context-api] provided by failure.
18
19 ```rust
20 #[derive(Debug)]
21 struct MyError {
22 inner: Context<MyErrorKind>,
23 }
24
25 #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
26 enum MyErrorKind {
27 // A plain enum with no data in any of its variants
28 //
29 // For example:
30 #[fail(display = "A contextual error message.")]
31 OneVariant,
32 // ...
33 }
34 ```
35
36 Unfortunately, it is not easy to correctly derive `Fail` for `MyError` so that
37 it delegates things to its inner `Context`. You should write those impls
38 yourself:
39
40 ```rust
41 impl Fail for MyError {
42 fn cause(&self) -> Option<&Fail> {
43 self.inner.cause()
44 }
45
46 fn backtrace(&self) -> Option<&Backtrace> {
47 self.inner.backtrace()
48 }
49 }
50
51 impl Display for MyError {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 Display::fmt(&self.inner, f)
54 }
55 }
56 ```
57
58 You should also provide some conversions and accessors, to go between a
59 Context, your ErrorKind, and your Error:
60
61 ```rust
62 impl MyError {
63 pub fn kind(&self) -> MyErrorKind {
64 *self.inner.get_context()
65 }
66 }
67
68 impl From<MyErrorKind> for MyError {
69 fn from(kind: MyErrorKind) -> MyError {
70 MyError { inner: Context::new(kind) }
71 }
72 }
73
74 impl From<Context<MyErrorKind>> for MyError {
75 fn from(inner: Context<MyErrorKind>) -> MyError {
76 MyError { inner: inner }
77 }
78 }
79 ```
80
81 With this code set up, you can use the context method from failure to apply
82 your ErrorKind to errors in underlying libraries:
83
84 ```rust
85 perform_some_io().context(ErrorKind::NetworkFailure)?;
86 ```
87
88 You can also directly throw `ErrorKind` without an underlying error when
89 appropriate:
90
91 ```rust
92 Err(ErrorKind::DomainSpecificError)?
93 ```
94
95 ### What should your ErrorKind contain?
96
97 Your error kind probably should not carry data - and if it does, it should only
98 carry stateless data types that provide additional information about what the
99 `ErrorKind` means. This way, your `ErrorKind` can be `Eq`, making it
100 easy to use as a way of comparing errors.
101
102 Your ErrorKind is a way of providing information about what errors mean
103 appropriate to the level of abstraction that your library operates at. As some
104 examples:
105
106 - If your library expects to read from the user's `Cargo.toml`, you might have
107 a `InvalidCargoToml` variant, to capture what `io::Error` and `toml::Error`
108 mean in the context of your library.
109 - If your library does both file system activity and network activity, you
110 might have `Filesystem` and `Network` variants, to divide up the `io::Error`s
111 between which system in particular failed.
112
113 Exactly what semantic information is appropriate depends entirely on what this
114 bit of code is intended to do.
115
116 ## When might you use this pattern?
117
118 The most likely use cases for this pattern are mid-layer which perform a
119 function that requires many dependencies, and that are intended to be used in
120 production. Libraries with few dependencies do not need to manage many
121 underlying error types and can probably suffice with a simpler [custom
122 failure][custom-fail]. Applications that know they are almost always just going
123 to log these errors can get away with [using the Error type][use-error] rather
124 than managing extra context information.
125
126 That said, when you need to provide the most expressive information about an
127 error possible, this can be a good approach.
128
129 ## Caveats on this pattern
130
131 This pattern is the most involved pattern documented in this book. It involves
132 a lot of boilerplate to set up (which may be automated away eventually), and it
133 requires you to apply a contextual message to every underlying error that is
134 thrown inside your code. It can be a lot of work to maintain this pattern.
135
136 Additionally, like the Error type, the Context type may use an allocation and a
137 dynamic dispatch internally. If you know this is too expensive for your use
138 case, you should not use this pattern.
139
140 [use-error]: ./use-error.html
141 [custom-fail]: ./custom-fail.html
142 [context-api]: https://boats.gitlab.io/failure/doc/failure/struct.Context.html