]> git.proxmox.com Git - rustc.git/blobdiff - src/doc/book/nostarch/chapter09.md
New upstream version 1.63.0+dfsg1
[rustc.git] / src / doc / book / nostarch / chapter09.md
index 54f0afceaa40044bb30b7becbbfa9066540488c7..693dc1be0d859b2feb6252aea04743ea0ac52daa 100644 (file)
@@ -21,10 +21,6 @@ likely just want to report the problem to the user and retry the operation.
 Unrecoverable errors are always symptoms of bugs, like trying to access a
 location beyond the end of an array, and so we want to immediately stop the
 program.
-<!-- so what do we want to do with unrecoverable ones, panic (or maybe just
-"exit the program")? /LC -->
-<!-- Yes, we want to stop the program, which is what panic does. I've opted for
-"stop" here since we haven't really defined "panic" yet. /Carol -->
 
 Most languages don’t distinguish between these two kinds of errors and handle
 both in the same way, using mechanisms such as exceptions. Rust doesn’t have
@@ -38,11 +34,13 @@ execution.
 ## Unrecoverable Errors with `panic!`
 
 Sometimes, bad things happen in your code, and there’s nothing you can do about
-it. In these cases, Rust has the `panic!` macro. When the `panic!` macro
-executes, your program will print a failure message, unwind and clean up the
-stack, and then quit. We’ll commonly invoke a panic when a bug of some kind has
-been detected and it’s not clear how to handle the problem at the time we’re
-writing our program.
+it. In these cases, Rust has the `panic!` macro. There are two ways to cause a
+panic in practice: by taking an action that causes our code to panic (such as
+accessing an array past the end) or by explicitly calling the `panic!` macro.
+In both cases, we cause a panic in our program. By default, these panics will
+print a failure message, unwind, clean up the stack, and quit. Via an
+environment variable, you can also have Rust display the call stack when a
+panic occurs to make it easier to track down the source of the panic.
 
 <!-- does Rust invoke the panic, or do we? Or sometimes it can be either? /LC --->
 <!-- We will have done *something* through a combination of the code we've
@@ -50,16 +48,25 @@ written and the data the program gets at runtime. It *might* involve us
 literally typing `panic!` into our code, or it might be part of Rust that we're
 using that calls `panic!` for us because of something else we've done. Does
 that make sense? I've tried to clarify the last sentence a bit here /Carol -->
-
+<!---
+One way we could explain it is to say there are two ways to cause a panic in
+practice: by doing an action that causes our code to panic, like accessing an
+array past the end or dividing by zero, or by explicitly calling the `panic!`
+macro. In both cases, we cause a panic in our application. By default, these
+panics will unwind and clean up the stack. Via an environment setting, you can
+also have Rust display the call stack when a panic occurs to make it easier to
+track down the source of the panic.
+/JT --->
+<!-- I've taken JT's suggestion with some edits in the paragraph above /Carol
+-->
 > ### Unwinding the Stack or Aborting in Response to a Panic
 >
 > By default, when a panic occurs, the program starts *unwinding*, which
 > means Rust walks back up the stack and cleans up the data from each function
-> it encounters. However, this walking back and cleanup is a lot of work. Rust, therefore,
-> allows you to choose the alternative of immediately *aborting*, which ends the program without
-> cleaning up.
-> <!-- here it was unclear whether you're saying a Rust panic unwinds or whether it just immediately aborts; I've tried to clarify, can you check that it's accurate? /LC -->
-> <!-- A Rust panic unwinds by default; you can change the configuration setting if you want panic to immediately abort. What you put here is correct. /Carol -->
+> it encounters. However, this walking back and cleanup is a lot of work. Rust,
+> therefore, allows you to choose the alternative of immediately *aborting*,
+> which ends the program without cleaning up.
+>
 > Memory that the program was using will then need to be cleaned
 > up by the operating system. If in your project you need to make the resulting
 > binary as small as possible, you can switch from unwinding to aborting upon a
@@ -242,56 +249,40 @@ Filename: src/main.rs
 use std::fs::File;
 
 fn main() {
-    let f = File::open("hello.txt");
+    let greeting_file_result = File::open("hello.txt");
 }
 ```
 
 Listing 9-3: Opening a file
 
-How do we know `File::open` returns a `Result`? We could look at the standard
-library API documentation, or we could ask the compiler! If we give `f` a type
-annotation that we know is *not* the return type of the function and then try
-to compile the code, the compiler will tell us that the types don’t match. The
-error message will then tell us what the type of `f` *is*. Let’s try it! We
-know that the return type of `File::open` isn’t of type `u32`, so let’s change
-the `let f` statement to this:
-
-```
-    let f: u32 = File::open("hello.txt");
-```
-
-Attempting to compile now gives us the following output:
-
-```
-error[E0308]: mismatched types
- --> src/main.rs:4:18
-  |
-4 |     let f: u32 = File::open("hello.txt");
-  |            ---   ^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found enum `Result`
-  |            |
-  |            expected due to this
-  |
-  = note: expected type `u32`
-             found enum `Result<File, std::io::Error>`
-```
-
-This tells us the return type of the `File::open` function is a `Result<T, E>`.
-The generic parameter `T` has been filled in here with the type of the success
-value, `std::fs::File`, which is a file handle. The type of `E` used in the
-error value is `std::io::Error`.
-
-This return type means the call to `File::open` might succeed and return a file
-handle that we can read from or write to. The function call also might fail:
-for example, the file might not exist, or we might not have permission to
-access the file. The `File::open` function needs to have a way to tell us
-whether it succeeded or failed and at the same time give us either the file
-handle or error information. This information is exactly what the `Result` enum
-conveys.
-
-In the case where `File::open` succeeds, the value in the variable `f` will be
-an instance of `Ok` that contains a file handle. In the case where it fails,
-the value in `f` will be an instance of `Err` that contains more information
-about the kind of error that happened.
+<!---
+This brings up an interesting point - should we teach them to install
+rust-analyzer in the setup instructions? If so, then we can tell them to mouse
+over the name of what they want the typename of. The "assign something to i32 to
+have rustc tell you what it is" feels a bit like old style Rust.
+/JT --->
+<!-- I somewhat disagree here; not everyone uses IDE plugins. I'll see what JT
+says about mentioning rust-analyzer in chapter 1 rather than in the appendix...
+I am in favor of making the book shorter, though, so I've removed the parts
+about asking the compiler what the type of something is by deliberately
+annotating with the wrong type. /Carol -->
+
+The return type of `File::open` is a `Result<T, E>`. The generic parameter `T`
+has been filled in by the implementation of `File::open` with the type of the
+success value, `std::fs::File`, which is a file handle. The type of `E` used in
+the error value is `std::io::Error`. This return type means the call to
+`File::open` might succeed and return a file handle that we can read from or
+write to. The function call also might fail: for example, the file might not
+exist, or we might not have permission to access the file. The `File::open`
+function needs to have a way to tell us whether it succeeded or failed and at
+the same time give us either the file handle or error information. This
+information is exactly what the `Result` enum conveys.
+
+In the case where `File::open` succeeds, the value in the variable
+`greeting_file_result` will be an instance of `Ok` that contains a file handle.
+In the case where it fails, the value in `greeting_file_result` will be an
+instance of `Err` that contains more information about the kind of error that
+happened.
 
 We need to add to the code in Listing 9-3 to take different actions depending
 on the value `File::open` returns. Listing 9-4 shows one way to handle the
@@ -304,9 +295,9 @@ Filename: src/main.rs
 use std::fs::File;
 
 fn main() {
-    let f = File::open("hello.txt");
+    let greeting_file_result = File::open("hello.txt");
 
-    let f = match f {
+    let greeting_file = match greeting_file_result {
         Ok(file) => file,
         Err(error) => panic!("Problem opening the file: {:?}", error),
     };
@@ -322,7 +313,8 @@ before the `Ok` and `Err` variants in the `match` arms.
 
 When the result is `Ok`, this code will return the inner `file` value out of
 the `Ok` variant, and we then assign that file handle value to the variable
-`f`. After the `match`, we can use the file handle for reading or writing.
+`greeting_file`. After the `match`, we can use the file handle for reading or
+writing.
 
 The other arm of the `match` handles the case where we get an `Err` value from
 `File::open`. In this example, we’ve chosen to call the `panic!` macro. If
@@ -343,7 +335,7 @@ However, we want to take different actions for different failure reasons: if
 and return the handle to the new file. If `File::open` failed for any other
 reason—for example, because we didn’t have permission to open the file—we still
 want the code to `panic!` in the same way as it did in Listing 9-4. For this we
-add an inner `match` expression, shown in Listing 8-5.
+add an inner `match` expression, shown in Listing 9-5.
 
 Filename: src/main.rs
 
@@ -352,19 +344,19 @@ use std::fs::File;
 use std::io::ErrorKind;
 
 fn main() {
-    let f = File::open("hello.txt");
+    let greeting_file_result = File::open("hello.txt");
 
-    let f = match f {
+    let greeting_file = match greeting_file_result {
         Ok(file) => file,
         Err(error) => match error.kind() {
             ErrorKind::NotFound => match File::create("hello.txt") {
                 Ok(fc) => fc,
                 Err(e) => panic!("Problem creating the file: {:?}", e),
-            },
+            }
             other_error => {
-                panic!("Problem opening the file: {:?}", other_error)
+                panic!("Problem opening the file: {:?}", other_error);
             }
-        },
+        }
     };
 }
 ```
@@ -377,8 +369,8 @@ has a method `kind` that we can call to get an `io::ErrorKind` value. The enum
 `io::ErrorKind` is provided by the standard library and has variants
 representing the different kinds of errors that might result from an `io`
 operation. The variant we want to use is `ErrorKind::NotFound`, which indicates
-the file we’re trying to open doesn’t exist yet. So we match on `f`, but we
-also have an inner match on `error.kind()`.
+the file we’re trying to open doesn’t exist yet. So we match on
+`greeting_file_result`, but we also have an inner match on `error.kind()`.
 
 The condition we want to check in the inner match is whether the value returned
 by `error.kind()` is the `NotFound` variant of the `ErrorKind` enum. If it is,
@@ -388,30 +380,22 @@ file can’t be created, a different error message is printed. The second arm of
 the outer `match` stays the same, so the program panics on any error besides
 the missing file error.
 
-<!-- I'd suggest making this a box, as it's a bit of an aside. Feel free to re-title! /LC -->
-<!-- Sounds good! I've changed it to the blockquote markdown syntax to be consistent with how I've written other boxes in markdown. /Carol -->
-
 > ### Alternatives to Using `match` with `Result<T, E>`
 >
 > That’s a lot of `match`! The `match` expression is very useful but also very
 > much a primitive. In Chapter 13, you’ll learn about closures, which are used
 > with many of the methods defined on `Result<T, E>`. These methods can be more
 > concise than using `match` when handling `Result<T, E>` values in your code.
->
-> <!-- I've tried to clarify the connection of closures to match here, can you check it's fair to say? /LC -->
-> <!-- I've made some edits, please check to see if it's clear! /Carol -->
->
-> <!-- to clarify, is this next example using closures, or just a more concise way to do the same thing using some other technique? /LC -->
-> <!-- This next example uses closures and is a more concise way to do the same thing; I've tried to clarify /Carol -->
+
 > For example, here’s another way to write the same logic as shown in Listing
-> 9-5 but using closures and the `unwrap_or_else` method:
+> 9-5, this time using closures and the `unwrap_or_else` method:
 >
 > ```
 > use std::fs::File;
 > use std::io::ErrorKind;
 >
 > fn main() {
->     let f = File::open("hello.txt").unwrap_or_else(|error| {
+>     let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
 >         if error.kind() == ErrorKind::NotFound {
 >             File::create("hello.txt").unwrap_or_else(|error| {
 >                 panic!("Problem creating the file: {:?}", error);
@@ -445,7 +429,7 @@ Filename: src/main.rs
 use std::fs::File;
 
 fn main() {
-    let f = File::open("hello.txt").unwrap();
+    let greeting_file = File::open("hello.txt").unwrap();
 }
 ```
 
@@ -458,6 +442,15 @@ repr: Os { code: 2, message: "No such file or directory" } }',
 src/libcore/result.rs:906:4
 ```
 
+<!---
+More recent rustc versions give a bit better error here (specifically the location):
+
+thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
+Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:4:37
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+/JT --->
+<!-- I'll update the error output when we're in Word /Carol -->
+
 Similarly, the `expect` method lets us also choose the `panic!` error message.
 Using `expect` instead of `unwrap` and providing good error messages can convey
 your intent and make tracking down the source of a panic easier. The syntax of
@@ -469,7 +462,8 @@ Filename: src/main.rs
 use std::fs::File;
 
 fn main() {
-    let f = File::open("hello.txt").expect("Failed to open hello.txt");
+    let greeting_file = File::open("hello.txt")
+        .expect("hello.txt should be included in this project");
 }
 ```
 
@@ -479,15 +473,31 @@ will be the parameter that we pass to `expect`, rather than the default
 `panic!` message that `unwrap` uses. Here’s what it looks like:
 
 ```
-thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
+thread 'main' panicked at 'hello.txt should be included in this project: Error { repr: Os { code:
 2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
 ```
 
-Because this error message starts with the text we specified, `Failed to open
-hello.txt`, it will be easier to find where in the code this error message is
-coming from. If we use `unwrap` in multiple places, it can take more time to
-figure out exactly which `unwrap` is causing the panic because all `unwrap`
-calls that panic print the same message.
+<!---
+Ditto with the above:
+
+thread 'main' panicked at 'Failed to open hello.txt: Os { code: 2, kind: NotFound,
+message: "No such file or directory" }', src/main.rs:4:37
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+/JT --->
+<!-- I'll update the error output when we're in Word /Carol -->
+
+In production-quality code, most Rustaceans choose `expect` rather than
+`unwrap` and give more context about why the operation is expected to always
+succeed. That way, if your assumptions are ever proven wrong, you have more
+information to use in debugging.
+
+<!---
+Now that `unwrap` and `expect` give an improved file location, we may not
+need the paragraph above.
+/JT --->
+<!-- I've changed the paragraph above, as well as the text in the examaple
+usage of `expect`, to better reflect current best practices and the reasons for
+them. /Carol -->
 
 ### Propagating Errors
 
@@ -508,19 +518,19 @@ Filename: src/main.rs
 use std::fs::File;
 use std::io::{self, Read};
 
-fn read_username_from_file() -> Result<String, io::Error> {
-    let f = File::open("hello.txt");
+fn read_username_from_file() -> Result<String, io::Error> [1] {
+    let username_file_result = File::open("hello.txt"); [2]
 
-    let mut f = match f {
-        Ok(file) => file,
-        Err(e) => return Err(e),
+    let mut username_file [3] = match username_file_result {
+        Ok(file) => file, [4]
+        Err(e) => return Err(e), [5]
     };
 
-    let mut s = String::new();
+    let mut username = String::new(); [6]
 
-    match f.read_to_string(&mut s) {
-        Ok(_) => Ok(s),
-        Err(e) => Err(e),
+    match username_file.read_to_string(&mut username) [7] {
+        Ok(_) => Ok(username), [8]
+        Err(e) => Err(e), [9]
     }
 }
 ```
@@ -530,38 +540,55 @@ Listing 9-6: A function that returns errors to the calling code using `match`
 This function can be written in a much shorter way, but we’re going to start by
 doing a lot of it manually in order to explore error handling; at the end,
 we’ll show the shorter way. Let’s look at the return type of the function
-first: `Result<String, io::Error>`. This means the function is returning a
+first: `Result<String, io::Error>` [1]. This means the function is returning a
 value of the type `Result<T, E>` where the generic parameter `T` has been
 filled in with the concrete type `String`, and the generic type `E` has been
-filled in with the concrete type `io::Error`. If this function succeeds without
-any problems, the code that calls this function will receive an `Ok` value that
-holds a `String`—the username that this function read from the file. If this
-function encounters any problems, the calling code will receive an `Err` value
-that holds an instance of `io::Error` that contains more information about what
-the problems were. We chose `io::Error` as the return type of this function
-because that happens to be the type of the error value returned from both of
-the operations we’re calling in this function’s body that might fail: the
-`File::open` function and the `read_to_string` method.
-
-The body of the function starts by calling the `File::open` function. Then we
-handle the `Result` value with a `match` similar to the `match` in Listing 9-4.
-If `File::open` succeeds, the file handle in the pattern variable `file`
-becomes the value in the mutable variable `f` and the function continues. In
-the `Err` case, instead of calling `panic!`, we use the `return` keyword to
-return early out of the function entirely and pass the error value from
-`File::open`, now in the pattern variable `e`, back to the calling code as this
-function’s error value.
-
-So if we have a file handle in `f`, the function then creates a new `String` in
-variable `s` and calls the `read_to_string` method on the file handle in `f` to
-read the contents of the file into `s`. The `read_to_string` method also
-returns a `Result` because it might fail, even though `File::open` succeeded.
-So we need another `match` to handle that `Result`: if `read_to_string`
-succeeds, then our function has succeeded, and we return the username from the
-file that’s now in `s` wrapped in an `Ok`. If `read_to_string` fails, we return
-the error value in the same way that we returned the error value in the `match`
-that handled the return value of `File::open`. However, we don’t need to
-explicitly say `return`, because this is the last expression in the function.
+filled in with the concrete type `io::Error`.
+
+If this function succeeds without any problems, the code that calls this
+function will receive an `Ok` value that holds a `String`—the username that
+this function read from the file [8]. If this function encounters any problems,
+the calling code will receive an `Err` value that holds an instance of
+`io::Error` that contains more information about what the problems were. We
+chose `io::Error` as the return type of this function because that happens to
+be the type of the error value returned from both of the operations we’re
+calling in this function’s body that might fail: the `File::open` function [2]
+and the `read_to_string` method [7].
+
+The body of the function starts by calling the `File::open` function [2]. Then
+we handle the `Result` value with a `match` similar to the `match` in Listing
+9-4. If `File::open` succeeds, the file handle in the pattern variable `file`
+[4] becomes the value in the mutable variable `username_file` [3] and the
+function continues. In the `Err` case, instead of calling `panic!`, we use the
+`return` keyword to return early out of the function entirely and pass the
+error value from `File::open`, now in the pattern variable `e`, back to the
+calling code as this function’s error value [5].
+
+So if we have a file handle in `username_file`, the function then creates a new
+`String` in variable `username` [6] and calls the `read_to_string` method on
+the file handle in `username_file` to read the contents of the file into
+`username` [7]. The `read_to_string` method also returns a `Result` because it
+might fail, even though `File::open` succeeded. So we need another `match` to
+handle that `Result`: if `read_to_string` succeeds, then our function has
+succeeded, and we return the username from the file that’s now in `username`
+wrapped in an `Ok`. If `read_to_string` fails, we return the error value in the
+same way that we returned the error value in the `match` that handled the
+return value of `File::open`. However, we don’t need to explicitly say
+`return`, because this is the last expression in the function [9].
+
+<!---
+Style nit: I'm finding the above two paragraphs a bit difficult to read
+comfortably. I think one issue is that we're using a handful of single letter
+variable names while also trying to walk someone through an explanation of
+multiple concepts.
+
+Maybe just me? But feels like the above example might be explained a bit better
+if we used more complete variable names so the explanation could have a better
+flow (without trying to remember what each of the single-letter variables meant)
+/JT --->
+<!-- Totally valid! I've changed the variable names in this, previous, and
+following examples, broke up these paragraphs a bit, and added wingdings.
+/Carol -->
 
 The code that calls this code will then handle getting either an `Ok` value
 that contains a username or an `Err` value that contains an `io::Error`. It’s
@@ -589,10 +616,10 @@ use std::io;
 use std::io::Read;
 
 fn read_username_from_file() -> Result<String, io::Error> {
-    let mut f = File::open("hello.txt")?;
-    let mut s = String::new();
-    f.read_to_string(&mut s)?;
-    Ok(s)
+    let mut username_file = File::open("hello.txt")?;
+    let mut username = String::new();
+    username_file.read_to_string(&mut username)?;
+    Ok(username)
 }
 ```
 
@@ -610,20 +637,32 @@ code.
 There is a difference between what the `match` expression from Listing 9-6 does
 and what the `?` operator does: error values that have the `?` operator called
 on them go through the `from` function, defined in the `From` trait in the
-standard library, which is used to convert errors from one type into another.
+standard library, which is used to convert values from one type into another.
 When the `?` operator calls the `from` function, the error type received is
 converted into the error type defined in the return type of the current
 function. This is useful when a function returns one error type to represent
 all the ways a function might fail, even if parts might fail for many different
-reasons. As long as there’s an `impl From<OtherError> for ReturnedError` to
-define the conversion in the trait’s `from` function, the `?` operator takes
-care of calling the `from` function automatically.
+reasons.
+
+For example, we could change the `read_username_from_file` function in Listing
+9-7 to return a custom error type named `OurError` that we define. If we also
+define `impl From<io::Error> for OurError` to construct an instance of
+`OurError` from an `io::Error`, then the `?` operator calls in the body of
+`read_username_from_file` will call `from` and convert the error types without
+needing to add any more code to the function.
+
+<!---
+It's a bit fuzzy what `impl From<OtherError> for ReturnedError` means. We may
+want to use a more concrete example, like: `impl From<OurError> for io::Error`.
+/JT --->
+<!-- I've added a more concrete example here, but converting the other way,
+which I think is more likely in production code /Carol -->
 
 In the context of Listing 9-7, the `?` at the end of the `File::open` call will
-return the value inside an `Ok` to the variable `f`. If an error occurs, the
-`?` operator will return early out of the whole function and give any `Err`
-value to the calling code. The same thing applies to the `?` at the end of the
-`read_to_string` call.
+return the value inside an `Ok` to the variable `username_file`. If an error
+occurs, the `?` operator will return early out of the whole function and give
+any `Err` value to the calling code. The same thing applies to the `?` at the
+end of the `read_to_string` call.
 
 The `?` operator eliminates a lot of boilerplate and makes this function’s
 implementation simpler. We could even shorten this code further by chaining
@@ -637,24 +676,24 @@ use std::io;
 use std::io::Read;
 
 fn read_username_from_file() -> Result<String, io::Error> {
-    let mut s = String::new();
+    let mut username = String::new();
 
-    File::open("hello.txt")?.read_to_string(&mut s)?;
+    File::open("hello.txt")?.read_to_string(&mut username)?;
 
-    Ok(s)
+    Ok(username)
 }
 ```
 
 Listing 9-8: Chaining method calls after the `?` operator
 
-We’ve moved the creation of the new `String` in `s` to the beginning of the
-function; that part hasn’t changed. Instead of creating a variable `f`, we’ve
-chained the call to `read_to_string` directly onto the result of
-`File::open("hello.txt")?`. We still have a `?` at the end of the
-`read_to_string` call, and we still return an `Ok` value containing the
-username in `s` when both `File::open` and `read_to_string` succeed rather than
-returning errors. The functionality is again the same as in Listing 9-6 and
-Listing 9-7; this is just a different, more ergonomic way to write it.
+We’ve moved the creation of the new `String` in `username` to the beginning of
+the function; that part hasn’t changed. Instead of creating a variable
+`username_file`, we’ve chained the call to `read_to_string` directly onto the
+result of `File::open("hello.txt")?`. We still have a `?` at the end of the
+`read_to_string` call, and we still return an `Ok` value containing `username`
+when both `File::open` and `read_to_string` succeed rather than returning
+errors. The functionality is again the same as in Listing 9-6 and Listing 9-7;
+this is just a different, more ergonomic way to write it.
 
 Listing 9-9 shows a way to make this even shorter using `fs::read_to_string`.
 
@@ -693,11 +732,13 @@ In Listing 9-10, let’s look at the error we’ll get if we use the `?` operato
 in a `main` function with a return type incompatible with the type of the value
 we use `?` on:
 
+Filename: src/main.rs
+
 ```
 use std::fs::File;
 
 fn main() {
-    let f = File::open("hello.txt")?;
+    let greeting_file = File::open("hello.txt")?;
 }
 ```
 
@@ -724,9 +765,7 @@ error[E0277]: the `?` operator can only be used in a function that returns `Resu
 This error points out that we’re only allowed to use the `?` operator in a
 function that returns `Result`, `Option`, or another type that implements
 `FromResidual`.
-<!--- are we saying it is always the case that the return type must be Result etc, or in this specific case? I think the former, but wanted to check. /LC --->
-<!-- Yes, it is always the case that the return type of a function that uses `?` in its body must return  `Result`, `Option`, or another type that implements
-`FromResidual`. This has changed over Rust's history, and might change in the future... but this is what it is in the current state of Rust. /Carol -->
+
 To fix the error, you have two choices. One choice is to change the return type
 of your function to be compatible with the value you’re using the `?` operator
 on as long as you have no restrictions preventing that. The other technique is
@@ -794,21 +833,39 @@ use std::error::Error;
 use std::fs::File;
 
 fn main() -> Result<(), Box<dyn Error>> {
-    let f = File::open("hello.txt")?;
+    let greeting_file = File::open("hello.txt")?;
 
     Ok(())
 }
 ```
 
+<!---
+The move to `Box<dyn Error>` isn't unexpected for an experienced Rust
+developer, but I wonder if we should keep `std::io::Error` here to keep with
+the flow of the previous examples?
+
+I think my instinct was to mention this since we don't use the flexibility
+the trait object gives us. Instead, we switch to explaining how exit codes
+work with Result values.
+/JT --->
+<!-- The idea here was to give the reader code that will work in the future no
+matter what errors they're trying to return from main. If we put in
+std::io::Error, it'll work for this example, but probably not in the reader's
+own projects. I've added a sentence to the end of the paragraph after Listing
+9-12's caption to explain this thinking. /Carol -->
+
 Listing 9-12: Changing `main` to return `Result<(), E>` allows the use of the
 `?` operator on `Result` values
 
 The `Box<dyn Error>` type is a *trait object*, which we’ll talk about in the
 “Using Trait Objects that Allow for Values of Different Types” section in
 Chapter 17. For now, you can read `Box<dyn Error>` to mean “any kind of error.”
-Using `?` on a `Result` value in a `main` function with the error type
-`Box<dyn Error>` is allowed, because it allows any `Err` value to be returned
-early.
+Using `?` on a `Result` value in a `main` function with the error type `Box<dyn
+Error>` is allowed, because it allows any `Err` value to be returned early.
+Even though the body of this `main` function will only ever return errors of
+type `std::io::Error`, by specifying `Box<dyn Error>`, this signature will
+continue to be correct even if more code that returns other errors is added to
+the body of `main`.
 
 When a `main` function returns a `Result<(), E>`, the executable will
 exit with a value of `0` if `main` returns `Ok(())` and will exit with a
@@ -817,14 +874,10 @@ integers when they exit: programs that exit successfully return the integer
 `0`, and programs that error return some integer other than `0`. Rust also
 returns integers from executables to be compatible with this convention.
 
-<!-- oh, this is why you mention the C and Rust return values. Perhaps move that discussion here and keep it brief? /LC -->
-<!-- I've tried moving and clarifying! /Carol -->
-
 The `main` function may return any types that implement the
-`std::process::Termination` trait. As of this writing, the `Termination` trait
-is an unstable feature only available in Nightly Rust, so you can’t yet
-implement it for your own types in Stable Rust, but you might be able to
-someday!
+`std::process::Termination` trait, which contains a function `report` that
+returns an `ExitCode` Consult the standard library documentation for more
+information on implementing the `Termination` trait for your own types.
 
 Now that we’ve discussed the details of calling `panic!` or returning `Result`,
 let’s return to the topic of how to decide which is appropriate to use in which
@@ -868,23 +921,37 @@ happen.
 
 ### Cases in Which You Have More Information Than the Compiler
 
-It would also be appropriate to call `unwrap` when you have some other logic
-that ensures the `Result` will have an `Ok` value, but the logic isn’t
-something the compiler understands. You’ll still have a `Result` value that you
-need to handle: whatever operation you’re calling still has the possibility of
-failing in general, even though it’s logically impossible in your particular
-situation. If you can ensure by manually inspecting the code that you’ll never
-have an `Err` variant, it’s perfectly acceptable to call `unwrap`. Here’s an
-example:
+It would also be appropriate to call `unwrap` or `expect` when you have some
+other logic that ensures the `Result` will have an `Ok` value, but the logic
+isn’t something the compiler understands. You’ll still have a `Result` value
+that you need to handle: whatever operation you’re calling still has the
+possibility of failing in general, even though it’s logically impossible in
+your particular situation. If you can ensure by manually inspecting the code
+that you’ll never have an `Err` variant, it’s perfectly acceptable to call
+`unwrap`, and even better to document the reason you think you’ll never have an
+`Err` variant in the `expect` text. Here’s an example:
+
+<!---
+Some Rust devs may have a nuanced take on the above, myself included. I'd say
+you'd be safer to use `.expect(...)` and put as the argument the reason why it
+should never fail. If, in the future it ever *does* fail for some reason
+(probably as a result of many code fixes over time), then you've got a message
+to start with telling you what the original expectation was.
+/JT --->
+<!-- I agree with this and reinforcing this best practice; I've changed the
+`unwrap` to `expect` and demonstrated a good message. I still don't want to
+shame people too much for using `unwrap`, though. /Carol -->
 
 ```
 use std::net::IpAddr;
 
-let home: IpAddr = "127.0.0.1".parse().unwrap();
+let home: IpAddr = "127.0.0.1"
+    .parse()
+    .expect("Hardcoded IP address should be valid");
 ```
 
 We’re creating an `IpAddr` instance by parsing a hardcoded string. We can see
-that `127.0.0.1` is a valid IP address, so it’s acceptable to use `unwrap`
+that `127.0.0.1` is a valid IP address, so it’s acceptable to use `expect`
 here. However, having a hardcoded, valid string doesn’t change the return type
 of the `parse` method: we still get a `Result` value, and the compiler will
 still make us handle the `Result` as if the `Err` variant is a possibility
@@ -892,6 +959,9 @@ because the compiler isn’t smart enough to see that this string is always a
 valid IP address. If the IP address string came from a user rather than being
 hardcoded into the program and therefore *did* have a possibility of failure,
 we’d definitely want to handle the `Result` in a more robust way instead.
+Mentioning the assumption that this IP address is hardcoded will prompt us to
+change `expect` to better error handling code if in the future, we need to get
+the IP address from some other source instead.
 
 ### Guidelines for Error Handling
 
@@ -910,11 +980,32 @@ code—plus one or more of the following:
   work through an example of what we mean in the “Encoding States and Behavior
   as Types” section of Chapter 17.
 
-If someone calls your code and passes in values that don’t make sense, the best
-choice might be to call `panic!` and alert the person using your library to the
-bug in their code so they can fix it during development. Similarly, `panic!` is
-often appropriate if you’re calling external code that is out of your control
-and it returns an invalid state that you have no way of fixing.
+If someone calls your code and passes in values that don’t make sense, it’s
+best to return an error if you can so the user of the library can decide what
+they want to do in that case. However, in cases where continuing could be
+insecure or harmful, the best choice might be to call `panic!` and alert the
+person using your library to the bug in their code so they can fix it during
+development. Similarly, `panic!` is often appropriate if you’re calling
+external code that is out of your control and it returns an invalid state that
+you have no way of fixing.
+
+<!---
+Disagree a bit with the above. I don't think libraries should ever panic. They
+should always be written defensively so they can be used in a broader range of
+applications, which include applications where crashing could result in data
+loss.
+
+Rather than crashing, libraries can encode the reasons they failed based on the
+user's input into an error that can be returned to the user.
+
+In practice, the only time the application should absolutely crash is if
+continuing could bring harm to the user's machine, their data, filesystem, and
+so on. Otherwise, the user should just be given a warning that the operation
+couldn't be completed successfully, so they can take their next action. If we
+crash, unfortunately the user never gets that choice.
+/JT --->
+<!-- I think we actually agree here but the original text wasn't clear enough;
+I've edited. /Carol -->
 
 However, when failure is expected, it’s more appropriate to return a `Result`
 than to make a `panic!` call. Examples include a parser being given malformed
@@ -922,20 +1013,33 @@ data or an HTTP request returning a status that indicates you have hit a rate
 limit. In these cases, returning a `Result` indicates that failure is an
 expected possibility that the calling code must decide how to handle.
 
-When your code performs operations on values, your code should verify the
-values are valid first and panic if the values aren’t valid. This is mostly for
-safety reasons: attempting to operate on invalid data can expose your code to
-vulnerabilities. This is the main reason the standard library will call
-`panic!` if you attempt an out-of-bounds memory access: trying to access memory
-that doesn’t belong to the current data structure is a common security problem.
-Functions often have *contracts*: their behavior is only guaranteed if the
-inputs meet particular requirements. Panicking when the contract is violated
-makes sense because a contract violation always indicates a caller-side bug and
-it’s not a kind of error you want the calling code to have to explicitly
-handle. In fact, there’s no reasonable way for calling code to recover; the
-calling *programmers* need to fix the code. Contracts for a function,
-especially when a violation will cause a panic, should be explained in the API
-documentation for the function.
+When your code performs an operation that could put a user at risk if it’s
+called using invalid values, your code should verify the values are valid first
+and panic if the values aren’t valid. This is mostly for safety reasons:
+attempting to operate on invalid data can expose your code to vulnerabilities.
+This is the main reason the standard library will call `panic!` if you attempt
+an out-of-bounds memory access: trying to access memory that doesn’t belong to
+the current data structure is a common security problem. Functions often have
+*contracts*: their behavior is only guaranteed if the inputs meet particular
+requirements. Panicking when the contract is violated makes sense because a
+contract violation always indicates a caller-side bug and it’s not a kind of
+error you want the calling code to have to explicitly handle. In fact, there’s
+no reasonable way for calling code to recover; the calling *programmers* need
+to fix the code. Contracts for a function, especially when a violation will
+cause a panic, should be explained in the API documentation for the function.
+
+<!---
+The wording of the first sentence in the above paragraph reads like we should
+panic on invalid data, but in the previous paragraph we say malformed data
+should be a `Result`. The rest makes sense, where the spirit of when the stdlib
+panics is less about invalid data and more about when the user will be put at
+risk.
+/JT --->
+<!-- I think we were trying to draw a distinction between "malformed" and
+"invalid" values that perhaps wasn't very clear. I've tried to clarify by
+adding "could put a user at risk", but I don't really want to get into the
+specifics of this because only a subset of readers will be writing code like
+this... /Carol -->
 
 However, having lots of error checks in all of your functions would be verbose
 and annoying. Fortunately, you can use Rust’s type system (and thus the type
@@ -1024,6 +1128,22 @@ impl Guess {
 }
 ```
 
+<!---
+The above example feels a bit off to me. We talk earlier about user input being
+a prime candidate for recoverable errors, and then we talk about encoding only
+proper states in the type system. But this examples seems to work with user
+input and panic if it's not correct, rather than using recoverable errors or
+encoding the state into the type.
+
+Maybe you could have them guess rock/paper/scissors and encode the
+rock/paper/scissor as three enum values, and if they type something outside of
+that, we don't allow it. Otherwise we create an enum of that value.
+/JT --->
+<!-- The point about this listing panicking is valid, but I disagree a little.
+I think this is encoding only valid states into the type system. Also, Chapter
+11 builds on this example to show how to use `should_panic`, so I'm going to
+leave this the way it is. /Carol -->
+
 Listing 9-13: A `Guess` type that will only continue with values between 1 and
 100
 
@@ -1072,3 +1192,18 @@ situations will make your code more reliable in the face of inevitable problems.
 Now that you’ve seen useful ways that the standard library uses generics with
 the `Option` and `Result` enums, we’ll talk about how generics work and how you
 can use them in your code.
+
+<!---
+A meta comment: the coverage of `panic!` here feels helpful in terms of giving
+a more complete understanding of Rust, but in practice (and this may depend
+on domain), using `panic!` should be a fairly limited thing.
+
+Something I noticed we don't touch on but may want to is panic hooks, as
+unrecoverable errors isn't exactly true. You can recover from an unwinding
+panic if you need to code defensively against, say, a dependency panicking and
+you don't want your app to go down as a result.
+/JT --->
+<!-- Yeahhh I don't want to mention panic hooks, one because I don't think most
+people will need to think about them or implement one, and two because a subset
+of people will look at that and think "oh look, exception handling!" which...
+is not what it's for. /Carol -->