Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

start of error handling #134

Merged
merged 53 commits into from
Oct 31, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
a28453e
start of error handling
steveklabnik Jul 14, 2016
4723782
move towards vectors
steveklabnik Aug 19, 2016
e7b5943
error handling take two
steveklabnik Aug 23, 2016
e35e0e1
Remove 'creating your own error types'
steveklabnik Aug 25, 2016
80c9725
remove second backtrace
steveklabnik Aug 25, 2016
730fb1f
Move explanation up as per @aturon
steveklabnik Aug 25, 2016
3634c81
address some feedback
steveklabnik Aug 25, 2016
ad0fcf3
It's not always possible to handle this at compile time
steveklabnik Aug 25, 2016
0b7bb54
some more small things
steveklabnik Aug 26, 2016
71d4ab9
disable non-nightly build
steveklabnik Aug 26, 2016
bd18fff
don't put my dir in output names
steveklabnik Aug 26, 2016
6f3cf5c
Rename files since we decided errors is chapter 9
carols10cents Aug 31, 2016
9057190
Wording tweaks
carols10cents Aug 31, 2016
3f9dd9d
Explain the code we're going to try a bit more before showing it
carols10cents Aug 31, 2016
e33f886
Technically, panicing isn't the *only* thing we can do here...
carols10cents Aug 31, 2016
b86cf13
Word wrap
carols10cents Aug 31, 2016
f9b690e
Calling panic should be rare; this makes a nice segue
carols10cents Sep 1, 2016
6f581c3
Small wording tweaks
carols10cents Aug 31, 2016
e8cba59
Explain the type parameters a little since this is the first time
carols10cents Sep 1, 2016
0f5a5e6
Leave some notes about ghosting code differences
carols10cents Sep 1, 2016
b2929bf
Use bash syntax highlighting for console output
carols10cents Sep 1, 2016
79bac99
Use parens when referring to fns inline; clarify some things
carols10cents Sep 1, 2016
b88aad0
Make these code examples build off each other
carols10cents Sep 1, 2016
7095fd1
TODO; added some ideas for continuing recoverable section
carols10cents Sep 1, 2016
cd7932f
tools -> features
carols10cents Sep 1, 2016
9154816
splits -> groups
carols10cents Sep 1, 2016
2d40c73
More edits after more readings forever
carols10cents Sep 14, 2016
48c6890
Add a section on deciding how to handle errors
carols10cents Sep 14, 2016
c1c40d5
Rework intro error handling section
carols10cents Oct 11, 2016
e10d974
Update reason why we can't test beta rn
carols10cents Oct 11, 2016
da9422b
Add a sidebar on unwind vs abort
carols10cents Oct 11, 2016
6d2a2c4
Remove distraction about literal 100
carols10cents Oct 11, 2016
e98eab8
Reworking Result examples til I hit the horrible Carrier error
carols10cents Oct 12, 2016
48bed57
Use `try!` for the illustration of calling it in `main`
carols10cents Oct 12, 2016
1eaa218
Finish rearranging the Result section
carols10cents Oct 12, 2016
a3dfedb
Add a discussion of matching on error kind
carols10cents Oct 12, 2016
34b346c
Fix section headers
carols10cents Oct 12, 2016
8f17a2a
Don't talk about `main` in the default recommendation
carols10cents Oct 12, 2016
68bf232
Remove not-quite-accurate parts of this section
carols10cents Oct 12, 2016
261056e
Start of a section on good `unwrap` usage`
carols10cents Oct 12, 2016
9cb2218
Remove a "this" that's not clear
carols10cents Oct 12, 2016
b7a9b8a
Flesh out constructor contract example
carols10cents Oct 13, 2016
c00340c
Small tweaks to wording on another readthrough
carols10cents Oct 17, 2016
6e7eb0e
Discuss that it's ok to `unwrap` if you know it can't fail
carols10cents Oct 17, 2016
211aec8
Add listing numbers for long or referred-to code
carols10cents Oct 17, 2016
0fc14df
Add a summary
carols10cents Oct 17, 2016
2162339
Talk a bit about the contract of Guess
carols10cents Oct 17, 2016
3a8c5ef
Title case titles
carols10cents Oct 18, 2016
fdc98f4
Remove redundant 'file'
carols10cents Oct 18, 2016
0cd5b91
Spelling
carols10cents Oct 18, 2016
ee82377
Whitespace in code
carols10cents Oct 18, 2016
234e758
Make notes since we now know 1.14 will have the question mark
carols10cents Oct 18, 2016
3f72a06
Forward reference to api docs
carols10cents Oct 18, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ language: rust
cache: cargo
rust:
- nightly
- stable
# - beta
# ^ to be changed once question_mark is in beta: 1.14
# - stable
# ^ to be changed once question_mark becomes stable: 1.14
before_script:
- (cargo install mdbook || true)
script:
Expand Down
5 changes: 4 additions & 1 deletion src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
- [Strings]()
- [`HashMap<K, V>`]()

- [Error Handling]()
- [Error Handling](ch09-00-error-handling.md)
- [Unrecoverable Errors with `panic!`](ch09-01-unrecoverable-errors-with-panic.md)
- [Recoverable Errors with `Result`](ch09-02-recoverable-errors-with-result.md)
- [To `panic!` or Not To `panic!`](ch09-03-to-panic-or-not-to-panic.md)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this title is in ... title case. The other two sub-titles aren't. Is that just because this is a Shakespeare reference? if so, I'm cool with that, just making sure the others don't need to also be title case

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The others do need to be in title case. Good catch!


- [Lifetimes]()

Expand Down
26 changes: 26 additions & 0 deletions src/ch09-00-error-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Error Handling

Rust's focus on reliability extends to the area of error handling. Errors are a
fact of life in software, so Rust has a number of features that you can use to
handle situations in which something bad happens. In many cases, Rust requires
you to acknowledge the possibility of an error occurring and take some action
in that situation. This makes your program more robust by eliminating the
possibility of unexpected errors only being discovered after you've deployed
your code to production.

Rust groups errors into two major kinds: errors that are *recoverable*, and
errors that are *unrecoverable*. Recoverable errors are problems like a file not
being found, where it's usually reasonable to report that problem to the user
and retry the operation. Unrecoverable errors are problems like trying to
access a location beyond the end of an array, and these are always symptoms of
bugs.

Most languages do not distinguish between the two kinds of errors, so they
handle both kinds in the same way using mechanisms like exceptions. Rust
doesn't have exceptions. Instead, it has the value `Result<T, E>` to return in
the case of recoverable errors and the `panic!` macro that stops execution when
it encounters unrecoverable errors. This chapter will cover the more
straightforward case of calling `panic!` first. Then, we'll talk about
returning `Result<T, E>` values and calling functions that return `Result<T,
E>`. Finally, we'll discuss considerations to take into account when deciding
whether to try to recover from an error or to stop execution.
145 changes: 145 additions & 0 deletions src/ch09-01-unrecoverable-errors-with-panic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
## Unrecoverable Errors with `panic!`

Sometimes, bad things happen, and there's nothing that you can do about it. For
these cases, Rust has a macro, `panic!`. When this macro executes, your program
will print a failure message, unwind and clean up the stack, and then quit. The
most common reason for this is when a bug of some kind has been detected, and
it's not clear how to handle the error.

<!-- PROD: START BOX -->

> #### Unwinding
> By default, when a `panic!` happens in Rust, the program starts
> *unwinding*, which means Rust walks back up the stack and cleans up the data
> from each function it encounters. Doing that walking and cleanup is a lot of
> work. The alternative is to immediately `abort`, which ends the program
> without cleaning up. Memory that the program was using will need to be cleaned
> up by the operating system. If you're in a situation where you need to make
> the resulting binary as small as possible, you can switch from unwinding on
> panic to aborting on panic by adding `panic = 'abort'` to the appropriate
> `[profile]` sections in your `Cargo.toml`.

<!-- PROD: END BOX -->

Let's try out calling `panic!()` with a simple program:

```rust,should_panic
fn main() {
panic!("crash and burn");
}
```

If you run it, you'll see something like this:

```bash
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished debug [unoptimized + debuginfo] target(s) in 0.25 secs
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
```

There are three lines of error message here. The first line shows our panic
message and the place in our source code where the panic occurred:
`src/main.rs`, line two.

But that only shows us the exact line that called `panic!`. That's not always
useful. Let's look at another example to see what it's like when a `panic!`
call comes from code we call instead of from our code directly:

```rust,should_panic
fn main() {
let v = vec![1, 2, 3];

v[100];
}
```

We're attempting to access the hundredth element of our vector, but it only has
three elements. In this situation, Rust will panic. Using `[]` is supposed to
return an element. If you pass `[]` an invalid index, though, there's no
element that Rust could return here that would be correct.

Other languages like C will attempt to give you exactly what you asked for in
this situation, even though it isn't what you want: you'll get whatever is at
the location in memory that would correspond to that element in the vector,
even though the memory doesn't belong to the vector. This is called a *buffer
overread*, and can lead to security vulnerabilities if an attacker can
manipulate the index in such a way as to read data they shouldn't be allowed to
that is stored after the array.

In order to protect your program from this sort of vulnerability, if you try to
read an element at an index that doesn't exist, Rust will stop execution and
refuse to continue with an invalid value. Let's try it and see:

```bash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there is a global policy on this, but I don't think the bash syntax highlighting does any good. It's rather confusing IMO. (this affects other lines too)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm. I'm going to file a new issue on this... I'm mostly doing it so that we get a gray box around this block in the mdbook output, but we could probably tweak our CSS to get plain text blocks to have a gray box too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@steveklabnik i think the bash syntax highlighting is highlighting as if these blocks were bash scripts, not bash shell output, which is what we're putting in these blocks. It changes color of words in a seemingly arbitrary way for our output :-/

$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished debug [unoptimized + debuginfo] target(s) in 0.27 secs
Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
100', ../src/libcollections/vec.rs:1265
note: Run with `RUST_BACKTRACE=1` for a backtrace.
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
```

This points at a file we didn't write, `../src/libcollections/vec.rs`. That's
the implementation of `Vec<T>` in the standard library. While it's easy to see
in this short program where the error was, it would be nicer if we could have
Rust tell us what line in our program caused the error.

That's what the next line, the `note` is about. If we set the `RUST_BACKTRACE`
environment variable, we'll get a backtrace of exactly how the error happend.
Let's try that. Listing 9-1 shows the output:

```bash
$ RUST_BACKTRACE=1 cargo run
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
100', ../src/libcollections/vec.rs:1265
stack backtrace:
1: 0x560956150ae9 -
std::sys::backtrace::tracing::imp::write::h482d45d91246faa2
2: 0x56095615345c -
std::panicking::default_hook::_{{closure}}::h89158f66286b674e
3: 0x56095615291e - std::panicking::default_hook::h9e30d428ee3b0c43
4: 0x560956152f88 -
std::panicking::rust_panic_with_hook::h2224f33fb7bf2f4c
5: 0x560956152e22 - std::panicking::begin_panic::hcb11a4dc6d779ae5
6: 0x560956152d50 - std::panicking::begin_panic_fmt::h310416c62f3935b3
7: 0x560956152cd1 - rust_begin_unwind
8: 0x560956188a2f - core::panicking::panic_fmt::hc5789f4e80194729
9: 0x5609561889d3 -
core::panicking::panic_bounds_check::hb2d969c3cc11ed08
10: 0x56095614c075 - _<collections..vec..Vec<T> as
core..ops..Index<usize>>::index::hb9f10d3dadbe8101
at ../src/libcollections/vec.rs:1265
11: 0x56095614c134 - panic::main::h2d7d3751fb8705e2
at /projects/panic/src/main.rs:4
12: 0x56095615af46 - __rust_maybe_catch_panic
13: 0x560956152082 - std::rt::lang_start::h352a66f5026f54bd
14: 0x56095614c1b3 - main
15: 0x7f75b88ed72f - __libc_start_main
16: 0x56095614b3c8 - _start
17: 0x0 - <unknown>
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
```

<caption>
Listing 9-1: The backtrace generated by a call to `panic!` displayed when
the environment variable `RUST_BACKTRACE` is set
</caption>

That's a lot of output! Line 11 of the backtrace points to the line in our
project causing the problem: `src/main.rs` line four. The key to reading the
backtrace is to start from the top and read until we see files that we wrote:
that's where the problem originated. If we didn't want our program to panic
here, this line is where we would start investigating in order to figure out
how we got to this location with values that caused the panic.

Now that we've covered how to `panic!` to stop our code's execution and how to
debug a `panic!`, let's look at how to instead return and use recoverable
errors with `Result`.
Loading