1
1
# Signaling errors [ RFC]
2
2
3
- ### For unrecoverable errors, ` fail! ` .
3
+ Errors fall into one of three categories:
4
4
5
- Unrecoverable errors are rare and catastrophic. Since there is no way
6
- to cleanly exit the operation that discovered the error, there is no
7
- sensible return value. Instead, ` fail ` unwinds the task's stack. See
8
- "Handling Errors" below for guidance on how to use tasks to isolate
9
- unrecoverable errors.
5
+ * Catastrophic errors, e.g. out of memory.
6
+ * Unpreventable errors, e.g. file not found.
7
+ * Preventable errors, e.g. wrong input encoding, index out of bounds.
8
+
9
+ The signaling strategy is determined by the error category.
10
+
11
+ ## Catastrophic errors
12
+
13
+ An error is _ catastrophic_ if there is no meaningful way for the current task to
14
+ continue after the error occurs.
15
+
16
+ Catastrophic errors are _ extremely_ rare, especially outside of ` libstd ` .
17
+
18
+ ** Canonical examples** : out of memory, stack overflow.
19
+
20
+ > ** [ FIXME] ** These are believed to be the _ only_ catastrophic errors in
21
+ > Rust/libstd at the moment. We should confirm, and say so.
22
+
23
+ ### For catastrophic errors, use ` fail! ` .
24
+
25
+ Invoking ` fail! ` causes the current task to fail, which:
26
+
27
+ * unwinds the task's stack
28
+ * poisons any channels or other synchronization connecting the task to others
10
29
11
30
For example,
12
31
[ ` rt::heap::allocate ` ] ( http://static.rust-lang.org/doc/master/std/rt/heap/fn.allocate.html )
13
32
fails the task if allocation fails.
14
33
15
- ### For recoverable but unavoidable errors, use ` Result ` .
34
+ Task failure does not mean that the entire program must abort.
35
+ For example, by breaking a program into a parent task that monitors its
36
+ children, the program _ can_ meaningfully recover from task failures at the
37
+ global level. See [ the error handling section] ( handling.md ) for guidance
38
+ on working with task failure.
39
+
40
+ ## Unpreventable errors
41
+
42
+ An error is _ unpreventable_ if it is caused by circumstances out of the
43
+ program's control.
44
+
45
+ Unpreventable errors are common for operations that involve I/O or other access
46
+ to shared resources.
47
+
48
+ ** Canonical example** : file not found.
49
+
50
+ ### For unpreventable errors, use ` Result ` .
51
+
52
+ The
53
+ [ ` Result<T,E> ` type] ( http://static.rust-lang.org/doc/master/std/result/index.html )
54
+ represents either a success (yielding ` T ` ) or failure (yielding ` E ` ). By
55
+ returning a ` Result ` , a function allows its clients to discover and react to
56
+ external circumstances that are obstructing an operation.
16
57
17
58
The ` E ` component in ` Result<T,E> ` should convey details about the external
18
59
circumstances that caused the error. Use an ` enum ` when multiple kinds of errors
@@ -24,67 +65,109 @@ uses the `Result` type pervasively, with
24
65
[ ` IoError ` ] ( http://static.rust-lang.org/doc/master/std/io/struct.IoError.html )
25
66
providing details when an error occurs.
26
67
27
- ### For recoverable and avoidable errors, prefer ` Result ` .
68
+ ## Preventable errors
69
+
70
+ An error is _ preventable_ if its absence can be guaranteed by restricting the
71
+ arguments given to the operation.
28
72
29
- Avoidable errors can only arise when a function places additional expectations
30
- on its input beyond its static type. When the error is also recoverable, there
31
- are two choices.
73
+ Preventable errors are common for operations that impose constraints on their
74
+ parameters beyond their static type.
32
75
33
- #### Preferred: returning a ` Result `
76
+ ** Canonical examples** : wrong input encoding, index out of bounds.
77
+
78
+ ### For preventable errors, prefer ` Result ` .
79
+
80
+ Preventable errors present API designers with a choice:
81
+ * Permit erroneous input, return ` Result ` , and use the ` Err ` variant to inform
82
+ the client of the error.
83
+ * Treat erroneous input as a _ contract violation_ (i.e., assertion failure) and ` fail! ` .
84
+
85
+ The right choice depends on the _ overall design_ of an API: some APIs make it
86
+ very easy to obtain and work with valid inputs, in which case working with ` Result ` would be a needless distraction.
87
+
88
+ But when in doubt, prefer ` Result ` .
89
+
90
+ #### When to return a ` Result `
91
+
92
+ For preventable errors in APIs where
93
+
94
+ * ensuring input validity ahead of time is difficult or expensive, or
95
+ * validity checking is a useful byproduct of attempting an operation,
96
+
97
+ return a ` Result ` .
34
98
35
99
Following the principle of
36
100
[ returning useful intermediate results] ( ../features/functions-and-methods/output.md ) ,
37
- the function should return a ` Result ` value with an error component saying
38
- exactly where things went wrong. This information is typically produced when
39
- validating or consuming input anyway; returning it to the client allows for
40
- better error reporting and possibly recovery.
101
+ the ` Result ` 's error component should give detail about which part of the input
102
+ was invalid -- information that is typically produced during input validation or
103
+ processing anyway.
41
104
42
105
For example, a function ` from_str ` for parsing into a given type should return a
43
- ` Result ` with error component giving the location or span of parsing
44
- failure.
106
+ ` Result ` with error component giving the location or span of parsing failure.
45
107
46
- Clients can treat the function's contract as a kind of assertion by
47
- calling ` unwrap ` on the ` Result ` , which will produce a failure _ in the
48
- client's code_ when the client breaks the contract .
108
+ If clients believe they are providing valid input, they can use ` unwrap ` on the
109
+ ` Result ` to assert as much; this will produce a failure _ in the client's
110
+ code_ if things go wrong .
49
111
50
- #### Failing.
112
+ #### When to ` fail! `
51
113
52
- While returning a ` Result ` makes for a very clear interface, it can be
53
- burdensome:
114
+ For preventable errors in APIs where
54
115
55
- * Clients have to ` unwrap ` the result to assert that they have
56
- satisfied the function's contract.
57
- * The function has to internally propagate the ` Result ` , and possibly
58
- perform extra cleanup.
116
+ * input validity can be easily checked, or
117
+ * parts of the API naturally produce valid inputs for other parts, or
118
+ * invalid inputs clearly represent a logic error,
59
119
60
- Sometimes avoidable, recoverable errors are very rare and clearly
61
- indicate a logic error rather than bad input. Such errors are also
62
- likely to cause task failure at some point (usually because clients
63
- would just ` unwrap ` ).
120
+ using ` fail! ` is acceptable.
64
121
65
- In those rare cases, it is acceptable to invoke ` fail! ` when the error
66
- is discovered. The expectations on the input then become a strong part
67
- of the function's contract, and violating them is always a preventable
68
- bug. The failure conditions should be clearly stated in a ` Failure `
69
- section of the function's doc comment.
122
+ The expectations on the input then become a _ contract_ for the function, and
123
+ violating them is akin to an assertion failure -- a bug. The failure conditions
124
+ should be clearly stated in a ` Failure ` section of the function's doc comment.
70
125
71
- For example, slice indexing fails on an out of bounds error, which is
72
- recoverable and avoidable but nearly always represents a bug (and is
73
- hopefully rare).
126
+ The benefit of using ` fail! ` is that the signatures of the API's functions are
127
+ simpler, and using the API does not require the client to constantly ` unwrap ` .
128
+
129
+ For example, slice indexing fails on an out of bounds error, which is a
130
+ preventable error and nearly always represents a bug. Moreover, most uses of
131
+ array indexing naturally incorporate a bounds check already, e.g. looping over
132
+ array indices.
74
133
75
134
> ** [ FIXME] ** ` std ` also contains some more dubious examples, like the
76
135
> [ ` to_c_str ` ] ( http://static.rust-lang.org/doc/master/std/c_str/trait.ToCStr.html#tymethod.to_c_str )
77
136
> method, which fails when the string contains an interior
78
137
> ` null ` . What do we want to say about those?
79
138
80
- ### Fallible convenience methods. [ OPEN]
139
+ #### Do not provide both ` Result ` and ` fail! ` variants. [ RFC]
140
+
141
+ An API should not provide both ` Result ` -producing and ` fail ` ing versions of an
142
+ operation. It should provide just the ` Result ` version, allowing clients to use
143
+ ` try! ` or ` unwrap ` instead as needed.
144
+
145
+ There is one exception to this rule, however. Some APIs are strongly oriented
146
+ around failure, in the sense that their functions/methods are explicitly
147
+ intended as assertions. If there is no other way to check in advance for the
148
+ validity of invoking an operation ` foo ` , however, the API may provide a
149
+ ` checked_foo ` variant that returns a ` Result ` .
150
+
151
+ The main examples in ` libstd ` providing both variants are:
152
+ * Channels, which are the primary point of failure propagation between tasks. As
153
+ such, calling ` recv() ` is an _ assertion_ that the other end of the channel is
154
+ still alive, which will propagate failures from the other end of the
155
+ channel. On the other hand, since there is no separate way to atomically test
156
+ whether the other end has hung up, channels provide a ` recv_opt ` variant that
157
+ produces a ` Result ` .
158
+
159
+ > ** [ FIXME] ** The ` _opt ` suffix needs to be replaced by a ` checked_ ` prefix.
160
+
161
+
162
+ * ` RefCell ` , which provides a dynamic version of the borrowing rules. Calling
163
+ the ` borrow() ` method is intended as an assertion that the cell is in a
164
+ borrowable state, and will ` fail! ` otherwise. On the other hand, there is no
165
+ separate way to check the state of the ` RefCell ` , so the module provides a
166
+ ` try_borrow ` variant that produces a ` Result ` .
81
167
82
- > ** [ OPEN] ** This is a big open issue: when should APIs provide
83
- > convenience methods that amount to ` unwrap ` ping a ` Result ` ? E.g.,
84
- > the ` RefCell ` type has ` borrow ` and ` try_borrow ` , with ` .borrow() `
85
- > equivalent to ` try_borrow().unwrap() ` .
168
+ > ** [ FIXME] ** The ` try_ ` prefix needs to be replaced by a ` checked_ ` prefix.
86
169
87
- ### Avoid ` Option ` for error signaling.
170
+ ### Avoid ` Option ` for error signaling. [ RFC ]
88
171
89
172
The ` Option ` type should be reserved for cases that do not represent errors, but
90
173
rather possible outcomes for well-formed inputs under normal circumstances.
@@ -93,8 +176,8 @@ For example,
93
176
[ the ` Vec::pop ` method] ( http://static.rust-lang.org/doc/master/std/vec/struct.Vec.html )
94
177
returns an ` Option ` , yielding ` None ` when the vector is empty.
95
178
96
- Even when there is no interesting error information to return, prefer `Result<T,
97
- ()>` to ` Option<T >` as a way of signaling that the ` Err` case represents an
179
+ Even when there is no interesting error information to return, prefer
180
+ ` Result<T, ()>` to ` Option<T> ` as a way of signaling that the ` Err ` case represents an
98
181
erroneous outcome, not a normal outcome.
99
182
100
183
Rather than providing a ` try_frob ` function yielding an ` Option ` , provide a
0 commit comments