]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | % Signaling errors [RFC #236] |
2 | ||
3 | > The guidelines below were approved by [RFC #236](https://github.com/rust-lang/rfcs/pull/236). | |
4 | ||
5 | Errors fall into one of three categories: | |
6 | ||
7 | * Catastrophic errors, e.g. out-of-memory. | |
8 | * Contract violations, e.g. wrong input encoding, index out of bounds. | |
9 | * Obstructions, e.g. file not found, parse error. | |
10 | ||
11 | The basic principle of the convention is that: | |
12 | ||
13 | * Catastrophic errors and programming errors (bugs) can and should only be | |
bd371182 | 14 | recovered at a *coarse grain*, i.e. a thread boundary. |
85aaf69f SL |
15 | * Obstructions preventing an operation should be reported at a maximally *fine |
16 | grain* -- to the immediate invoker of the operation. | |
17 | ||
18 | ## Catastrophic errors | |
19 | ||
bd371182 | 20 | An error is _catastrophic_ if there is no meaningful way for the current thread to |
85aaf69f SL |
21 | continue after the error occurs. |
22 | ||
23 | Catastrophic errors are _extremely_ rare, especially outside of `libstd`. | |
24 | ||
25 | **Canonical examples**: out of memory, stack overflow. | |
26 | ||
27 | ### For catastrophic errors, panic | |
28 | ||
29 | For errors like stack overflow, Rust currently aborts the process, but | |
30 | could in principle panic, which (in the best case) would allow | |
bd371182 | 31 | reporting and recovery from a supervisory thread. |
85aaf69f SL |
32 | |
33 | ## Contract violations | |
34 | ||
35 | An API may define a contract that goes beyond the type checking enforced by the | |
36 | compiler. For example, slices support an indexing operation, with the contract | |
37 | that the supplied index must be in bounds. | |
38 | ||
39 | Contracts can be complex and involve more than a single function invocation. For | |
40 | example, the `RefCell` type requires that `borrow_mut` not be called until all | |
41 | existing borrows have been relinquished. | |
42 | ||
43 | ### For contract violations, panic | |
44 | ||
45 | A contract violation is always a bug, and for bugs we follow the Erlang | |
46 | philosophy of "let it crash": we assume that software *will* have bugs, and we | |
bd371182 | 47 | design coarse-grained thread boundaries to report, and perhaps recover, from these |
85aaf69f SL |
48 | bugs. |
49 | ||
50 | ### Contract design | |
51 | ||
52 | One subtle aspect of these guidelines is that the contract for a function is | |
53 | chosen by an API designer -- and so the designer also determines what counts as | |
54 | a violation. | |
55 | ||
56 | This RFC does not attempt to give hard-and-fast rules for designing | |
57 | contracts. However, here are some rough guidelines: | |
58 | ||
59 | * Prefer expressing contracts through static types whenever possible. | |
60 | ||
61 | * It *must* be possible to write code that uses the API without violating the | |
62 | contract. | |
63 | ||
64 | * Contracts are most justified when violations are *inarguably* bugs -- but this | |
65 | is surprisingly rare. | |
66 | ||
67 | * Consider whether the API client could benefit from the contract-checking | |
68 | logic. The checks may be expensive. Or there may be useful programming | |
69 | patterns where the client does not want to check inputs before hand, but would | |
70 | rather attempt the operation and then find out whether the inputs were invalid. | |
71 | ||
72 | * When a contract violation is the *only* kind of error a function may encounter | |
73 | -- i.e., there are no obstructions to its success other than "bad" inputs -- | |
74 | using `Result` or `Option` instead is especially warranted. Clients can then use | |
75 | `unwrap` to assert that they have passed valid input, or re-use the error | |
76 | checking done by the API for their own purposes. | |
77 | ||
78 | * When in doubt, use loose contracts and instead return a `Result` or `Option`. | |
79 | ||
80 | ## Obstructions | |
81 | ||
82 | An operation is *obstructed* if it cannot be completed for some reason, even | |
83 | though the operation's contract has been satisfied. Obstructed operations may | |
84 | have (documented!) side effects -- they are not required to roll back after | |
85 | encountering an obstruction. However, they should leave the data structures in | |
86 | a "coherent" state (satisfying their invariants, continuing to guarantee safety, | |
87 | etc.). | |
88 | ||
89 | Obstructions may involve external conditions (e.g., I/O), or they may involve | |
90 | aspects of the input that are not covered by the contract. | |
91 | ||
92 | **Canonical examples**: file not found, parse error. | |
93 | ||
94 | ### For obstructions, use `Result` | |
95 | ||
96 | The | |
e9174d1e | 97 | [`Result<T,E>` type](https://doc.rust-lang.org/stable/std/result/index.html) |
85aaf69f SL |
98 | represents either a success (yielding `T`) or failure (yielding `E`). By |
99 | returning a `Result`, a function allows its clients to discover and react to | |
100 | obstructions in a fine-grained way. | |
101 | ||
102 | #### What about `Option`? | |
103 | ||
104 | The `Option` type should not be used for "obstructed" operations; it | |
105 | should only be used when a `None` return value could be considered a | |
106 | "successful" execution of the operation. | |
107 | ||
108 | This is of course a somewhat subjective question, but a good litmus | |
109 | test is: would a reasonable client ever ignore the result? The | |
110 | `Result` type provides a lint that ensures the result is actually | |
111 | inspected, while `Option` does not, and this difference of behavior | |
112 | can help when deciding between the two types. | |
113 | ||
114 | Another litmus test: can the operation be understood as asking a | |
115 | question (possibly with sideeffects)? Operations like `pop` on a | |
116 | vector can be viewed as asking for the contents of the first element, | |
117 | with the side effect of removing it if it exists -- with an `Option` | |
118 | return value. | |
119 | ||
120 | ## Do not provide both `Result` and `panic!` variants. | |
121 | ||
122 | An API should not provide both `Result`-producing and `panic`king versions of an | |
123 | operation. It should provide just the `Result` version, allowing clients to use | |
124 | `try!` or `unwrap` instead as needed. This is part of the general pattern of | |
125 | cutting down on redundant variants by instead using method chaining. |