]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | ## Recoverable Errors with `Result` |
2 | ||
3 | Most errors aren’t serious enough to require the program to stop entirely. | |
4 | Sometimes, when a function fails, it’s for a reason that we can easily | |
5 | interpret and respond to. For example, if we try to open a file and that | |
6 | operation fails because the file doesn’t exist, we might want to create the | |
7 | file instead of terminating the process. | |
8 | ||
ea8adc8c XL |
9 | Recall in Chapter 2 in the on “[Handling Potential Failure with the `Result` |
10 | Type][handle_failure]<!-- ignore -->” section that the `Result` enum is defined | |
cc61c64b XL |
11 | as having two variants, `Ok` and `Err`, as follows: |
12 | ||
13 | [handle_failure]: ch02-00-guessing-game-tutorial.html#handling-potential-failure-with-the-result-type | |
14 | ||
15 | ```rust | |
16 | enum Result<T, E> { | |
17 | Ok(T), | |
18 | Err(E), | |
19 | } | |
20 | ``` | |
21 | ||
ea8adc8c | 22 | The `T` and `E` are generic type parameters: we’ll discuss generics in more |
cc61c64b XL |
23 | detail in Chapter 10. What you need to know right now is that `T` represents |
24 | the type of the value that will be returned in a success case within the `Ok` | |
25 | variant, and `E` represents the type of the error that will be returned in a | |
26 | failure case within the `Err` variant. Because `Result` has these generic type | |
27 | parameters, we can use the `Result` type and the functions that the standard | |
28 | library has defined on it in many different situations where the successful | |
29 | value and error value we want to return may differ. | |
30 | ||
31 | Let’s call a function that returns a `Result` value because the function could | |
ea8adc8c | 32 | fail: in Listing 9-3 we try to open a file: |
cc61c64b XL |
33 | |
34 | <span class="filename">Filename: src/main.rs</span> | |
35 | ||
36 | ```rust | |
37 | use std::fs::File; | |
38 | ||
39 | fn main() { | |
40 | let f = File::open("hello.txt"); | |
41 | } | |
42 | ``` | |
43 | ||
ea8adc8c | 44 | <span class="caption">Listing 9-3: Opening a file</span> |
cc61c64b XL |
45 | |
46 | How do we know `File::open` returns a `Result`? We could look at the standard | |
47 | library API documentation, or we could ask the compiler! If we give `f` a type | |
ea8adc8c | 48 | annotation of a type that we know the return type of the function is *not* and |
cc61c64b | 49 | then we try to compile the code, the compiler will tell us that the types don’t |
ea8adc8c | 50 | match. The error message will then tell us what the type of `f` *is*. Let’s try |
cc61c64b | 51 | it: we know that the return type of `File::open` isn’t of type `u32`, so let’s |
ea8adc8c | 52 | change the `let f` statement to this: |
cc61c64b XL |
53 | |
54 | ```rust,ignore | |
55 | let f: u32 = File::open("hello.txt"); | |
56 | ``` | |
57 | ||
ea8adc8c | 58 | Attempting to compile now gives us the following output: |
cc61c64b XL |
59 | |
60 | ```text | |
61 | error[E0308]: mismatched types | |
62 | --> src/main.rs:4:18 | |
63 | | | |
64 | 4 | let f: u32 = File::open("hello.txt"); | |
65 | | ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum | |
66 | `std::result::Result` | |
67 | | | |
68 | = note: expected type `u32` | |
69 | = note: found type `std::result::Result<std::fs::File, std::io::Error>` | |
70 | ``` | |
71 | ||
72 | This tells us the return type of the `File::open` function is a `Result<T, E>`. | |
73 | The generic parameter `T` has been filled in here with the type of the success | |
74 | value, `std::fs::File`, which is a file handle. The type of `E` used in the | |
75 | error value is `std::io::Error`. | |
76 | ||
77 | This return type means the call to `File::open` might succeed and return to us | |
78 | a file handle that we can read from or write to. The function call also might | |
ea8adc8c | 79 | fail: for example, the file might not exist or we might not have permission to |
cc61c64b | 80 | access the file. The `File::open` function needs to have a way to tell us |
ea8adc8c | 81 | whether it succeeded or failed and at the same time give us either the file |
cc61c64b XL |
82 | handle or error information. This information is exactly what the `Result` enum |
83 | conveys. | |
84 | ||
85 | In the case where `File::open` succeeds, the value we will have in the variable | |
86 | `f` will be an instance of `Ok` that contains a file handle. In the case where | |
87 | it fails, the value in `f` will be an instance of `Err` that contains more | |
88 | information about the kind of error that happened. | |
89 | ||
ea8adc8c XL |
90 | We need to add to the code in Listing 9-3 to take different actions depending |
91 | on the value `File::open` returned. Listing 9-4 shows one way to handle the | |
92 | `Result` using a basic tool: the `match` expression that we discussed in | |
cc61c64b XL |
93 | Chapter 6. |
94 | ||
95 | <span class="filename">Filename: src/main.rs</span> | |
96 | ||
97 | ```rust,should_panic | |
98 | use std::fs::File; | |
99 | ||
100 | fn main() { | |
101 | let f = File::open("hello.txt"); | |
102 | ||
103 | let f = match f { | |
104 | Ok(file) => file, | |
105 | Err(error) => { | |
106 | panic!("There was a problem opening the file: {:?}", error) | |
107 | }, | |
108 | }; | |
109 | } | |
110 | ``` | |
111 | ||
ea8adc8c | 112 | <span class="caption">Listing 9-4: Using a `match` expression to handle the |
cc61c64b XL |
113 | `Result` variants we might have</span> |
114 | ||
115 | Note that, like the `Option` enum, the `Result` enum and its variants have been | |
116 | imported in the prelude, so we don’t need to specify `Result::` before the `Ok` | |
117 | and `Err` variants in the `match` arms. | |
118 | ||
119 | Here we tell Rust that when the result is `Ok`, return the inner `file` value | |
120 | out of the `Ok` variant, and we then assign that file handle value to the | |
121 | variable `f`. After the `match`, we can then use the file handle for reading or | |
122 | writing. | |
123 | ||
124 | The other arm of the `match` handles the case where we get an `Err` value from | |
125 | `File::open`. In this example, we’ve chosen to call the `panic!` macro. If | |
126 | there’s no file named *hello.txt* in our current directory and we run this | |
127 | code, we’ll see the following output from the `panic!` macro: | |
128 | ||
129 | ```text | |
130 | thread 'main' panicked at 'There was a problem opening the file: Error { repr: | |
131 | Os { code: 2, message: "No such file or directory" } }', src/main.rs:8 | |
132 | ``` | |
133 | ||
ea8adc8c XL |
134 | As usual, this output tells us exactly what has gone wrong. |
135 | ||
cc61c64b XL |
136 | ### Matching on Different Errors |
137 | ||
ea8adc8c XL |
138 | The code in Listing 9-4 will `panic!` no matter the reason that `File::open` |
139 | failed. What we want to do instead is take different actions for different | |
140 | failure reasons: if `File::open` failed because the file doesn’t exist, we want | |
141 | to create the file and return the handle to the new file. If `File::open` | |
142 | failed for any other reason, for example because we didn’t have permission to | |
143 | open the file, we still want the code to `panic!` in the same way as it did in | |
144 | Listing 9-4. Look at Listing 9-5, which adds another arm to the `match`: | |
cc61c64b XL |
145 | |
146 | <span class="filename">Filename: src/main.rs</span> | |
147 | ||
ea8adc8c XL |
148 | <!-- ignore this test because otherwise it creates hello.txt which causes other |
149 | tests to fail lol --> | |
150 | ||
cc61c64b XL |
151 | ```rust,ignore |
152 | use std::fs::File; | |
153 | use std::io::ErrorKind; | |
154 | ||
155 | fn main() { | |
156 | let f = File::open("hello.txt"); | |
157 | ||
158 | let f = match f { | |
159 | Ok(file) => file, | |
160 | Err(ref error) if error.kind() == ErrorKind::NotFound => { | |
161 | match File::create("hello.txt") { | |
162 | Ok(fc) => fc, | |
163 | Err(e) => { | |
164 | panic!( | |
165 | "Tried to create file but there was a problem: {:?}", | |
166 | e | |
167 | ) | |
168 | }, | |
169 | } | |
170 | }, | |
171 | Err(error) => { | |
172 | panic!( | |
173 | "There was a problem opening the file: {:?}", | |
174 | error | |
175 | ) | |
176 | }, | |
177 | }; | |
178 | } | |
179 | ``` | |
180 | ||
ea8adc8c | 181 | <span class="caption">Listing 9-5: Handling different kinds of errors in |
cc61c64b XL |
182 | different ways</span> |
183 | ||
184 | The type of the value that `File::open` returns inside the `Err` variant is | |
185 | `io::Error`, which is a struct provided by the standard library. This struct | |
186 | has a method `kind` that we can call to get an `io::ErrorKind` value. | |
187 | `io::ErrorKind` is an enum provided by the standard library that has variants | |
188 | representing the different kinds of errors that might result from an `io` | |
ea8adc8c XL |
189 | operation. The variant we want to use is `ErrorKind::NotFound`, which indicates |
190 | the file we’re trying to open doesn’t exist yet. | |
cc61c64b XL |
191 | |
192 | The condition `if error.kind() == ErrorKind::NotFound` is called a *match | |
193 | guard*: it’s an extra condition on a `match` arm that further refines the arm’s | |
ea8adc8c XL |
194 | pattern. This condition must be true for that arm’s code to be run; otherwise, |
195 | the pattern matching will move on to consider the next arm in the `match`. The | |
196 | `ref` in the pattern is needed so `error` is not moved into the guard condition | |
197 | but is merely referenced by it. The reason `ref` is used to take a reference in | |
198 | a pattern instead of `&` will be covered in detail in Chapter 18. In short, in | |
199 | the context of a pattern, `&` matches a reference and gives us its value, but | |
200 | `ref` matches a value and gives us a reference to it. | |
cc61c64b XL |
201 | |
202 | The condition we want to check in the match guard is whether the value returned | |
203 | by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is, | |
ea8adc8c XL |
204 | we try to create the file with `File::create`. However, because `File::create` |
205 | could also fail, we need to add an inner `match` statement as well. When the | |
cc61c64b | 206 | file can’t be opened, a different error message will be printed. The last arm |
ea8adc8c XL |
207 | of the outer `match` stays the same so the program panics on any error besides |
208 | the missing file error. | |
cc61c64b XL |
209 | |
210 | ### Shortcuts for Panic on Error: `unwrap` and `expect` | |
211 | ||
212 | Using `match` works well enough, but it can be a bit verbose and doesn’t always | |
213 | communicate intent well. The `Result<T, E>` type has many helper methods | |
ea8adc8c | 214 | defined on it to do various tasks. One of those methods, called `unwrap`, is a |
cc61c64b | 215 | shortcut method that is implemented just like the `match` statement we wrote in |
ea8adc8c | 216 | Listing 9-4. If the `Result` value is the `Ok` variant, `unwrap` will return |
cc61c64b | 217 | the value inside the `Ok`. If the `Result` is the `Err` variant, `unwrap` will |
ea8adc8c XL |
218 | call the `panic!` macro for us. Here is an example of `unwrap` in action: |
219 | ||
220 | <span class="filename">Filename: src/main.rs</span> | |
cc61c64b XL |
221 | |
222 | ```rust,should_panic | |
223 | use std::fs::File; | |
224 | ||
225 | fn main() { | |
226 | let f = File::open("hello.txt").unwrap(); | |
227 | } | |
228 | ``` | |
229 | ||
230 | If we run this code without a *hello.txt* file, we’ll see an error message from | |
231 | the `panic!` call that the `unwrap` method makes: | |
232 | ||
233 | ```text | |
234 | thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error { | |
235 | repr: Os { code: 2, message: "No such file or directory" } }', | |
236 | /stable-dist-rustc/build/src/libcore/result.rs:868 | |
237 | ``` | |
238 | ||
ea8adc8c XL |
239 | Another method, `expect`, which is similar to `unwrap`, lets us also choose the |
240 | `panic!` error message. Using `expect` instead of `unwrap` and providing good | |
241 | error messages can convey your intent and make tracking down the source of a | |
242 | panic easier. The syntax of `expect` looks like this: | |
243 | ||
244 | <span class="filename">Filename: src/main.rs</span> | |
cc61c64b XL |
245 | |
246 | ```rust,should_panic | |
247 | use std::fs::File; | |
248 | ||
249 | fn main() { | |
250 | let f = File::open("hello.txt").expect("Failed to open hello.txt"); | |
251 | } | |
252 | ``` | |
253 | ||
254 | We use `expect` in the same way as `unwrap`: to return the file handle or call | |
ea8adc8c XL |
255 | the `panic!` macro. The error message used by `expect` in its call to `panic!` |
256 | will be the parameter that we pass to `expect`, rather than the default | |
cc61c64b XL |
257 | `panic!` message that `unwrap` uses. Here’s what it looks like: |
258 | ||
259 | ```text | |
260 | thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code: | |
261 | 2, message: "No such file or directory" } }', | |
262 | /stable-dist-rustc/build/src/libcore/result.rs:868 | |
263 | ``` | |
264 | ||
ea8adc8c XL |
265 | Because this error message starts with the text we specified, `Failed to open |
266 | hello.txt`, it will be easier to find where in the code this error message is | |
267 | coming from. If we use `unwrap` in multiple places, it can take more time to | |
268 | figure out exactly which `unwrap` is causing the panic because all `unwrap` | |
269 | calls that panic print the same message. | |
270 | ||
cc61c64b XL |
271 | ### Propagating Errors |
272 | ||
ea8adc8c XL |
273 | When you’re writing a function whose implementation calls something that might |
274 | fail, instead of handling the error within this function, you can return the | |
275 | error to the calling code so that it can decide what to do. This is known as | |
276 | *propagating* the error and gives more control to the calling code where there | |
cc61c64b XL |
277 | might be more information or logic that dictates how the error should be |
278 | handled than what you have available in the context of your code. | |
279 | ||
ea8adc8c | 280 | For example, Listing 9-6 shows a function that reads a username from a file. If |
cc61c64b XL |
281 | the file doesn’t exist or can’t be read, this function will return those errors |
282 | to the code that called this function: | |
283 | ||
ea8adc8c XL |
284 | <span class="filename">Filename: src/main.rs</span> |
285 | ||
cc61c64b XL |
286 | ```rust |
287 | use std::io; | |
288 | use std::io::Read; | |
289 | use std::fs::File; | |
290 | ||
291 | fn read_username_from_file() -> Result<String, io::Error> { | |
292 | let f = File::open("hello.txt"); | |
293 | ||
294 | let mut f = match f { | |
295 | Ok(file) => file, | |
296 | Err(e) => return Err(e), | |
297 | }; | |
298 | ||
299 | let mut s = String::new(); | |
300 | ||
301 | match f.read_to_string(&mut s) { | |
302 | Ok(_) => Ok(s), | |
303 | Err(e) => Err(e), | |
304 | } | |
305 | } | |
306 | ``` | |
307 | ||
ea8adc8c | 308 | <span class="caption">Listing 9-6: A function that returns errors to the |
cc61c64b XL |
309 | calling code using `match`</span> |
310 | ||
311 | Let’s look at the return type of the function first: `Result<String, | |
ea8adc8c | 312 | io::Error>`. This means the function is returning a value of the type |
cc61c64b XL |
313 | `Result<T, E>` where the generic parameter `T` has been filled in with the |
314 | concrete type `String`, and the generic type `E` has been filled in with the | |
315 | concrete type `io::Error`. If this function succeeds without any problems, the | |
ea8adc8c XL |
316 | code that calls this function will receive an `Ok` value that holds a |
317 | `String`—the username that this function read from the file. If this function | |
318 | encounters any problems, the code that calls this function will receive an | |
319 | `Err` value that holds an instance of `io::Error` that contains more | |
320 | information about what the problems were. We chose `io::Error` as the return | |
321 | type of this function because that happens to be the type of the error value | |
322 | returned from both of the operations we’re calling in this function’s body that | |
323 | might fail: the `File::open` function and the `read_to_string` method. | |
cc61c64b XL |
324 | |
325 | The body of the function starts by calling the `File::open` function. Then we | |
326 | handle the `Result` value returned with a `match` similar to the `match` in | |
ea8adc8c | 327 | Listing 9-4, only instead of calling `panic!` in the `Err` case, we return |
cc61c64b | 328 | early from this function and pass the error value from `File::open` back to the |
ea8adc8c XL |
329 | calling code as this function’s error value. If `File::open` succeeds, we store |
330 | the file handle in the variable `f` and continue. | |
cc61c64b XL |
331 | |
332 | Then we create a new `String` in variable `s` and call the `read_to_string` | |
ea8adc8c XL |
333 | method on the file handle in `f` to read the contents of the file into `s`. The |
334 | `read_to_string` method also returns a `Result` because it might fail, even | |
335 | though `File::open` succeeded. So we need another `match` to handle that | |
cc61c64b XL |
336 | `Result`: if `read_to_string` succeeds, then our function has succeeded, and we |
337 | return the username from the file that’s now in `s` wrapped in an `Ok`. If | |
338 | `read_to_string` fails, we return the error value in the same way that we | |
339 | returned the error value in the `match` that handled the return value of | |
ea8adc8c XL |
340 | `File::open`. However, we don’t need to explicitly say `return`, because this |
341 | is the last expression in the function. | |
cc61c64b XL |
342 | |
343 | The code that calls this code will then handle getting either an `Ok` value | |
344 | that contains a username or an `Err` value that contains an `io::Error`. We | |
ea8adc8c XL |
345 | don’t know what the calling code will do with those values. If the calling code |
346 | gets an `Err` value, it could call `panic!` and crash the program, use a | |
cc61c64b | 347 | default username, or look up the username from somewhere other than a file, for |
ea8adc8c XL |
348 | example. We don’t have enough information on what the calling code is actually |
349 | trying to do, so we propagate all the success or error information upwards for | |
350 | it to handle appropriately. | |
cc61c64b | 351 | |
ea8adc8c XL |
352 | This pattern of propagating errors is so common in Rust that Rust provides the |
353 | question mark operator `?` to make this easier. | |
cc61c64b | 354 | |
ea8adc8c | 355 | #### A Shortcut for Propagating Errors: `?` |
cc61c64b | 356 | |
ea8adc8c XL |
357 | Listing 9-7 shows an implementation of `read_username_from_file` that has the |
358 | same functionality as it had in Listing 9-6, but this implementation uses the | |
cc61c64b XL |
359 | question mark operator: |
360 | ||
ea8adc8c XL |
361 | <span class="filename">Filename: src/main.rs</span> |
362 | ||
cc61c64b XL |
363 | ```rust |
364 | use std::io; | |
365 | use std::io::Read; | |
366 | use std::fs::File; | |
367 | ||
368 | fn read_username_from_file() -> Result<String, io::Error> { | |
369 | let mut f = File::open("hello.txt")?; | |
370 | let mut s = String::new(); | |
371 | f.read_to_string(&mut s)?; | |
372 | Ok(s) | |
373 | } | |
374 | ``` | |
375 | ||
ea8adc8c | 376 | <span class="caption">Listing 9-7: A function that returns errors to the |
cc61c64b XL |
377 | calling code using `?`</span> |
378 | ||
ea8adc8c XL |
379 | The `?` placed after a `Result` value is defined to work in almost the same way |
380 | as the `match` expressions we defined to handle the `Result` values in Listing | |
381 | 9-6. If the value of the `Result` is an `Ok`, the value inside the `Ok` will | |
cc61c64b XL |
382 | get returned from this expression and the program will continue. If the value |
383 | is an `Err`, the value inside the `Err` will be returned from the whole | |
ea8adc8c XL |
384 | function as if we had used the `return` keyword so the error value gets |
385 | propagated to the calling code. | |
386 | ||
387 | The one difference between the `match` expression from Listing 9-6 and what the | |
388 | question mark operator does is that when using the question mark operator, | |
389 | error values go through the `from` function defined in the `From` trait in the | |
390 | standard library. Many error types implement the `from` function to convert an | |
391 | error of one type into an error of another type. When used by the question mark | |
392 | operator, the call to the `from` function converts the error type that the | |
393 | question mark operator gets into the error type defined in the return type of | |
394 | the current function that we’re using `?` in. This is useful when parts of a | |
395 | function might fail for many different reasons, but the function returns one | |
396 | error type that represents all the ways the function might fail. As long as | |
397 | each error type implements the `from` function to define how to convert itself | |
398 | to the returned error type, the question mark operator takes care of the | |
399 | conversion automatically. | |
400 | ||
401 | In the context of Listing 9-7, the `?` at the end of the `File::open` call will | |
cc61c64b | 402 | return the value inside an `Ok` to the variable `f`. If an error occurs, `?` |
ea8adc8c XL |
403 | will return early out of the whole function and give any `Err` value to the |
404 | calling code. The same thing applies to the `?` at the end of the | |
405 | `read_to_string` call. | |
cc61c64b XL |
406 | |
407 | The `?` eliminates a lot of boilerplate and makes this function’s | |
408 | implementation simpler. We could even shorten this code further by chaining | |
ea8adc8c XL |
409 | method calls immediately after the `?` as shown in Listing 9-8: |
410 | ||
411 | <span class="filename">Filename: src/main.rs</span> | |
cc61c64b XL |
412 | |
413 | ```rust | |
414 | use std::io; | |
415 | use std::io::Read; | |
416 | use std::fs::File; | |
417 | ||
418 | fn read_username_from_file() -> Result<String, io::Error> { | |
419 | let mut s = String::new(); | |
420 | ||
421 | File::open("hello.txt")?.read_to_string(&mut s)?; | |
422 | ||
423 | Ok(s) | |
424 | } | |
425 | ``` | |
426 | ||
ea8adc8c XL |
427 | <span class="caption">Listing 9-8: Chaining method calls after the question |
428 | mark operator</span> | |
429 | ||
cc61c64b XL |
430 | We’ve moved the creation of the new `String` in `s` to the beginning of the |
431 | function; that part hasn’t changed. Instead of creating a variable `f`, we’ve | |
432 | chained the call to `read_to_string` directly onto the result of | |
433 | `File::open("hello.txt")?`. We still have a `?` at the end of the | |
434 | `read_to_string` call, and we still return an `Ok` value containing the | |
435 | username in `s` when both `File::open` and `read_to_string` succeed rather than | |
ea8adc8c XL |
436 | returning errors. The functionality is again the same as in Listing 9-6 and |
437 | Listing 9-7; this is just a different, more ergonomic way to write it. | |
cc61c64b | 438 | |
ea8adc8c | 439 | #### `?` Can Only Be Used in Functions That Return Result |
cc61c64b XL |
440 | |
441 | The `?` can only be used in functions that have a return type of `Result`, | |
ea8adc8c XL |
442 | because it is defined to work in the same way as the `match` expression we |
443 | defined in Listing 9-6. The part of the `match` that requires a return type of | |
444 | `Result` is `return Err(e)`, so the return type of the function must be a | |
cc61c64b XL |
445 | `Result` to be compatible with this `return`. |
446 | ||
447 | Let’s look at what happens if we use `?` in the `main` function, which you’ll | |
448 | recall has a return type of `()`: | |
449 | ||
450 | ```rust,ignore | |
451 | use std::fs::File; | |
452 | ||
453 | fn main() { | |
454 | let f = File::open("hello.txt")?; | |
455 | } | |
456 | ``` | |
457 | ||
ea8adc8c | 458 | When we compile this code, we get the following error message: |
cc61c64b XL |
459 | |
460 | ```text | |
ea8adc8c XL |
461 | error[E0277]: the `?` operator can only be used in a function that returns |
462 | `Result` (or another type that implements `std::ops::Try`) | |
463 | --> src/main.rs:4:13 | |
cc61c64b | 464 | | |
ea8adc8c XL |
465 | 4 | let f = File::open("hello.txt")?; |
466 | | ------------------------ | |
467 | | | | |
468 | | cannot use the `?` operator in a function that returns `()` | |
469 | | in this macro invocation | |
cc61c64b | 470 | | |
ea8adc8c XL |
471 | = help: the trait `std::ops::Try` is not implemented for `()` |
472 | = note: required by `std::ops::Try::from_error` | |
cc61c64b XL |
473 | ``` |
474 | ||
ea8adc8c XL |
475 | This error points out that we’re only allowed to use the question mark operator |
476 | in a function that returns `Result`. In functions that don’t return `Result`, | |
477 | when you call other functions that return `Result`, you’ll need to use a | |
478 | `match` or one of the `Result` methods to handle it instead of using `?` to | |
479 | potentially propagate the error to the calling code. | |
cc61c64b XL |
480 | |
481 | Now that we’ve discussed the details of calling `panic!` or returning `Result`, | |
482 | let’s return to the topic of how to decide which is appropriate to use in which | |
483 | cases. |