]>
Commit | Line | Data |
---|---|---|
13cf67c4 XL |
1 | ## To `panic!` or Not to `panic!` |
2 | ||
3 | So how do you decide when you should call `panic!` and when you should return | |
4 | `Result`? When code panics, there’s no way to recover. You could call `panic!` | |
5 | for any error situation, whether there’s a possible way to recover or not, but | |
5099ac24 FG |
6 | then you’re making the decision that a situation is unrecoverable on behalf of |
7 | the calling code. When you choose to return a `Result` value, you give the | |
8 | calling code options. The calling code could choose to attempt to recover in a | |
9 | way that’s appropriate for its situation, or it could decide that an `Err` | |
10 | value in this case is unrecoverable, so it can call `panic!` and turn your | |
11 | recoverable error into an unrecoverable one. Therefore, returning `Result` is a | |
12 | good default choice when you’re defining a function that might fail. | |
13 | ||
14 | In situations such as examples, prototype code, and tests, it’s more | |
15 | appropriate to write code that panics instead of returning a `Result`. Let’s | |
16 | explore why, then discuss situations in which the compiler can’t tell that | |
17 | failure is impossible, but you as a human can. The chapter will conclude with | |
18 | some general guidelines on how to decide whether to panic in library code. | |
13cf67c4 XL |
19 | |
20 | ### Examples, Prototype Code, and Tests | |
21 | ||
5099ac24 FG |
22 | When you’re writing an example to illustrate some concept, also including robust |
23 | error-handling code can make the example less clear. In | |
13cf67c4 XL |
24 | examples, it’s understood that a call to a method like `unwrap` that could |
25 | panic is meant as a placeholder for the way you’d want your application to | |
26 | handle errors, which can differ based on what the rest of your code is doing. | |
27 | ||
28 | Similarly, the `unwrap` and `expect` methods are very handy when prototyping, | |
29 | before you’re ready to decide how to handle errors. They leave clear markers in | |
30 | your code for when you’re ready to make your program more robust. | |
31 | ||
32 | If a method call fails in a test, you’d want the whole test to fail, even if | |
33 | that method isn’t the functionality under test. Because `panic!` is how a test | |
34 | is marked as a failure, calling `unwrap` or `expect` is exactly what should | |
35 | happen. | |
36 | ||
37 | ### Cases in Which You Have More Information Than the Compiler | |
38 | ||
923072b8 FG |
39 | It would also be appropriate to call `unwrap` or `expect` when you have some |
40 | other logic that ensures the `Result` will have an `Ok` value, but the logic | |
41 | isn’t something the compiler understands. You’ll still have a `Result` value | |
42 | that you need to handle: whatever operation you’re calling still has the | |
43 | possibility of failing in general, even though it’s logically impossible in | |
44 | your particular situation. If you can ensure by manually inspecting the code | |
45 | that you’ll never have an `Err` variant, it’s perfectly acceptable to call | |
46 | `unwrap`, and even better to document the reason you think you’ll never have an | |
47 | `Err` variant in the `expect` text. Here’s an example: | |
13cf67c4 XL |
48 | |
49 | ```rust | |
74b04a01 | 50 | {{#rustdoc_include ../listings/ch09-error-handling/no-listing-08-unwrap-that-cant-fail/src/main.rs:here}} |
13cf67c4 XL |
51 | ``` |
52 | ||
53 | We’re creating an `IpAddr` instance by parsing a hardcoded string. We can see | |
923072b8 | 54 | that `127.0.0.1` is a valid IP address, so it’s acceptable to use `expect` |
13cf67c4 XL |
55 | here. However, having a hardcoded, valid string doesn’t change the return type |
56 | of the `parse` method: we still get a `Result` value, and the compiler will | |
57 | still make us handle the `Result` as if the `Err` variant is a possibility | |
58 | because the compiler isn’t smart enough to see that this string is always a | |
59 | valid IP address. If the IP address string came from a user rather than being | |
60 | hardcoded into the program and therefore *did* have a possibility of failure, | |
61 | we’d definitely want to handle the `Result` in a more robust way instead. | |
923072b8 FG |
62 | Mentioning the assumption that this IP address is hardcoded will prompt us to |
63 | change `expect` to better error handling code if in the future, we need to get | |
64 | the IP address from some other source instead. | |
13cf67c4 XL |
65 | |
66 | ### Guidelines for Error Handling | |
67 | ||
68 | It’s advisable to have your code panic when it’s possible that your code | |
69 | could end up in a bad state. In this context, a *bad state* is when some | |
70 | assumption, guarantee, contract, or invariant has been broken, such as when | |
71 | invalid values, contradictory values, or missing values are passed to your | |
72 | code—plus one or more of the following: | |
73 | ||
3c0e092e XL |
74 | * The bad state is something that is unexpected, as opposed to something that |
75 | will likely happen occasionally, like a user entering data in the wrong | |
76 | format. | |
77 | * Your code after this point needs to rely on not being in this bad state, | |
78 | rather than checking for the problem at every step. | |
79 | * There’s not a good way to encode this information in the types you use. We’ll | |
80 | work through an example of what we mean in the [“Encoding States and Behavior | |
81 | as Types”][encoding]<!-- ignore --> section of Chapter 17. | |
13cf67c4 | 82 | |
923072b8 FG |
83 | If someone calls your code and passes in values that don’t make sense, it’s |
84 | best to return an error if you can so the user of the library can decide what | |
85 | they want to do in that case. However, in cases where continuing could be | |
86 | insecure or harmful, the best choice might be to call `panic!` and alert the | |
87 | person using your library to the bug in their code so they can fix it during | |
88 | development. Similarly, `panic!` is often appropriate if you’re calling | |
89 | external code that is out of your control and it returns an invalid state that | |
90 | you have no way of fixing. | |
13cf67c4 | 91 | |
532ac7d7 | 92 | However, when failure is expected, it’s more appropriate to return a `Result` |
13cf67c4 XL |
93 | than to make a `panic!` call. Examples include a parser being given malformed |
94 | data or an HTTP request returning a status that indicates you have hit a rate | |
95 | limit. In these cases, returning a `Result` indicates that failure is an | |
96 | expected possibility that the calling code must decide how to handle. | |
97 | ||
923072b8 FG |
98 | When your code performs an operation that could put a user at risk if it’s |
99 | called using invalid values, your code should verify the values are valid first | |
100 | and panic if the values aren’t valid. This is mostly for safety reasons: | |
101 | attempting to operate on invalid data can expose your code to vulnerabilities. | |
102 | This is the main reason the standard library will call `panic!` if you attempt | |
103 | an out-of-bounds memory access: trying to access memory that doesn’t belong to | |
104 | the current data structure is a common security problem. Functions often have | |
105 | *contracts*: their behavior is only guaranteed if the inputs meet particular | |
106 | requirements. Panicking when the contract is violated makes sense because a | |
107 | contract violation always indicates a caller-side bug and it’s not a kind of | |
108 | error you want the calling code to have to explicitly handle. In fact, there’s | |
109 | no reasonable way for calling code to recover; the calling *programmers* need | |
110 | to fix the code. Contracts for a function, especially when a violation will | |
111 | cause a panic, should be explained in the API documentation for the function. | |
13cf67c4 XL |
112 | |
113 | However, having lots of error checks in all of your functions would be verbose | |
114 | and annoying. Fortunately, you can use Rust’s type system (and thus the type | |
5099ac24 FG |
115 | checking done by the compiler) to do many of the checks for you. If your |
116 | function has a particular type as a parameter, you can proceed with your code’s | |
117 | logic knowing that the compiler has already ensured you have a valid value. For | |
13cf67c4 XL |
118 | example, if you have a type rather than an `Option`, your program expects to |
119 | have *something* rather than *nothing*. Your code then doesn’t have to handle | |
120 | two cases for the `Some` and `None` variants: it will only have one case for | |
121 | definitely having a value. Code trying to pass nothing to your function won’t | |
122 | even compile, so your function doesn’t have to check for that case at runtime. | |
123 | Another example is using an unsigned integer type such as `u32`, which ensures | |
124 | the parameter is never negative. | |
125 | ||
9fa01778 XL |
126 | ### Creating Custom Types for Validation |
127 | ||
13cf67c4 XL |
128 | Let’s take the idea of using Rust’s type system to ensure we have a valid value |
129 | one step further and look at creating a custom type for validation. Recall the | |
130 | guessing game in Chapter 2 in which our code asked the user to guess a number | |
131 | between 1 and 100. We never validated that the user’s guess was between those | |
132 | numbers before checking it against our secret number; we only validated that | |
133 | the guess was positive. In this case, the consequences were not very dire: our | |
134 | output of “Too high” or “Too low” would still be correct. But it would be a | |
135 | useful enhancement to guide the user toward valid guesses and have different | |
136 | behavior when a user guesses a number that’s out of range versus when a user | |
137 | types, for example, letters instead. | |
138 | ||
139 | One way to do this would be to parse the guess as an `i32` instead of only a | |
140 | `u32` to allow potentially negative numbers, and then add a check for the | |
141 | number being in range, like so: | |
142 | ||
143 | ```rust,ignore | |
74b04a01 | 144 | {{#rustdoc_include ../listings/ch09-error-handling/no-listing-09-guess-out-of-range/src/main.rs:here}} |
13cf67c4 XL |
145 | ``` |
146 | ||
147 | The `if` expression checks whether our value is out of range, tells the user | |
148 | about the problem, and calls `continue` to start the next iteration of the loop | |
149 | and ask for another guess. After the `if` expression, we can proceed with the | |
150 | comparisons between `guess` and the secret number knowing that `guess` is | |
151 | between 1 and 100. | |
152 | ||
153 | However, this is not an ideal solution: if it was absolutely critical that the | |
154 | program only operated on values between 1 and 100, and it had many functions | |
155 | with this requirement, having a check like this in every function would be | |
156 | tedious (and might impact performance). | |
157 | ||
158 | Instead, we can make a new type and put the validations in a function to create | |
159 | an instance of the type rather than repeating the validations everywhere. That | |
160 | way, it’s safe for functions to use the new type in their signatures and | |
3c0e092e | 161 | confidently use the values they receive. Listing 9-13 shows one way to define a |
13cf67c4 | 162 | `Guess` type that will only create an instance of `Guess` if the `new` function |
9fa01778 | 163 | receives a value between 1 and 100. |
13cf67c4 | 164 | |
74b04a01 XL |
165 | <!-- Deliberately not using rustdoc_include here; the `main` function in the |
166 | file requires the `rand` crate. We do want to include it for reader | |
167 | experimentation purposes, but don't want to include it for rustdoc testing | |
168 | purposes. --> | |
169 | ||
13cf67c4 | 170 | ```rust |
3c0e092e | 171 | {{#include ../listings/ch09-error-handling/listing-09-13/src/main.rs:here}} |
13cf67c4 XL |
172 | ``` |
173 | ||
3c0e092e | 174 | <span class="caption">Listing 9-13: A `Guess` type that will only continue with |
13cf67c4 XL |
175 | values between 1 and 100</span> |
176 | ||
177 | First, we define a struct named `Guess` that has a field named `value` that | |
9fa01778 | 178 | holds an `i32`. This is where the number will be stored. |
13cf67c4 XL |
179 | |
180 | Then we implement an associated function named `new` on `Guess` that creates | |
181 | instances of `Guess` values. The `new` function is defined to have one | |
182 | parameter named `value` of type `i32` and to return a `Guess`. The code in the | |
183 | body of the `new` function tests `value` to make sure it’s between 1 and 100. | |
184 | If `value` doesn’t pass this test, we make a `panic!` call, which will alert | |
185 | the programmer who is writing the calling code that they have a bug they need | |
186 | to fix, because creating a `Guess` with a `value` outside this range would | |
187 | violate the contract that `Guess::new` is relying on. The conditions in which | |
188 | `Guess::new` might panic should be discussed in its public-facing API | |
189 | documentation; we’ll cover documentation conventions indicating the possibility | |
190 | of a `panic!` in the API documentation that you create in Chapter 14. If | |
191 | `value` does pass the test, we create a new `Guess` with its `value` field set | |
192 | to the `value` parameter and return the `Guess`. | |
193 | ||
194 | Next, we implement a method named `value` that borrows `self`, doesn’t have any | |
9fa01778 | 195 | other parameters, and returns an `i32`. This kind of method is sometimes called |
13cf67c4 XL |
196 | a *getter*, because its purpose is to get some data from its fields and return |
197 | it. This public method is necessary because the `value` field of the `Guess` | |
198 | struct is private. It’s important that the `value` field be private so code | |
199 | using the `Guess` struct is not allowed to set `value` directly: code outside | |
200 | the module *must* use the `Guess::new` function to create an instance of | |
201 | `Guess`, thereby ensuring there’s no way for a `Guess` to have a `value` that | |
202 | hasn’t been checked by the conditions in the `Guess::new` function. | |
203 | ||
204 | A function that has a parameter or returns only numbers between 1 and 100 could | |
9fa01778 | 205 | then declare in its signature that it takes or returns a `Guess` rather than an |
13cf67c4 XL |
206 | `i32` and wouldn’t need to do any additional checks in its body. |
207 | ||
208 | ## Summary | |
209 | ||
210 | Rust’s error handling features are designed to help you write more robust code. | |
211 | The `panic!` macro signals that your program is in a state it can’t handle and | |
212 | lets you tell the process to stop instead of trying to proceed with invalid or | |
213 | incorrect values. The `Result` enum uses Rust’s type system to indicate that | |
214 | operations might fail in a way that your code could recover from. You can use | |
215 | `Result` to tell code that calls your code that it needs to handle potential | |
216 | success or failure as well. Using `panic!` and `Result` in the appropriate | |
217 | situations will make your code more reliable in the face of inevitable problems. | |
218 | ||
219 | Now that you’ve seen useful ways that the standard library uses generics with | |
220 | the `Option` and `Result` enums, we’ll talk about how generics work and how you | |
221 | can use them in your code. | |
3c0e092e XL |
222 | |
223 | [encoding]: ch17-03-oo-design-patterns.html#encoding-states-and-behavior-as-types |