<style>
/* basic design */
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6,
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {
font-family: 'Meiryo UI', 'Source Sans Pro', Helvetica, sans-serif, 'Helvetica Neue', 'Helvetica', 'Arial', 'Hiragino Sans', 'ヒラギノ角ゴシック', YuGothic, 'Yu Gothic';
text-align: left;
line-height: 1.5;
letter-spacing: normal;
text-shadow: none;
word-wrap: break-word;
color: #AAA;
}
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {font-weight: bold;}
.reveal h1, .reveal h2, .reveal h3 {color: #2980b9;}
.reveal th {background: #DDD;}
.reveal section img {background:none; border:none; box-shadow:none; max-width: 95%; max-height: 95%;}
.reveal blockquote {width: 90%; padding: 0.5vw 3.0vw;}
.reveal table {margin: 1.0vw auto;}
.reveal code {line-height: 1.2;}
.reveal p, .reveal li {padding: 0vw; margin: 0vw;}
.reveal .box {margin: -0.5vw 1.5vw 2.0vw -1.5vw; padding: 0.5vw 1.5vw 0.5vw 1.5vw; background: #EEE; border-radius: 1.5vw;}
/* table design */
.reveal table {background: #f5f5f5;}
.reveal th {background: #444; color: #fff;}
.reveal td {position: relative; transition: all 300ms;}
.reveal tbody:hover td { color: transparent; text-shadow: 0 0 3px #AAA;}
.reveal tbody:hover tr:hover td {color: #444; text-shadow: 0 1px 0 #CCC;}
/* blockquote design */
.reveal blockquote {
width: 90%;
padding: 0.5vw 0 0.5vw 6.0vw;
font-style: italic;
background: #f5f5f5;
}
.reveal blockquote:before{
position: absolute;
top: 0.1vw;
left: 1vw;
content: "\f10d";
font-family: FontAwesome;
color: #2980b9;
font-size: 3.0vw;
}
/* font size */
.reveal h1 {font-size: 4.0vw;}
.reveal h2 {font-size: 3.5vw;}
.reveal h3 {font-size: 2.8vw;}
.reveal h4 {font-size: 2.6vw;}
.reveal h5 {font-size: 2.4vw;}
.reveal h6 {font-size: 2.2vw;}
.reveal section, .reveal table, .reveal li, .reveal blockquote, .reveal th, .reveal td, .reveal p {font-size: 2.0vw;}
.reveal code {font-size: 1.0vw;}
/* new color */
.red {color: #EE6557;}
.blue {color: #16A6B6;}
/* split slide */
#right {left: -18.33%; text-align: left; float: left; width: 50%; z-index: -10;}
#left {left: 31.25%; text-align: left; float: left; width: 50%; z-index: -10;}
</style>
<style>
/* specific design */
.reveal h2 {
padding: 0 1.5vw;
margin: 0.0vw 0 2.0vw -2.0vw;
border-left: solid 1.0vw #2980b9;
border-bottom: solid 0.6vw #d7d7d7;
}
</style>
<!-- --------------------------------------------------------------------------------------- -->
# Rust Study Session \#8
## Ch. 9: Error Handling
### 2020.09.25 - Salvatore La Bua
---
## Summary
- Unrecoverable errors
- Using the __*panic!*__ macro.
- Recoverable errors
- Using the __*Result\<T, E\>*__ enum.
- When to use panic! or Result
---
## Unrecoverable errors
### What are unrecoverable errors
- Errors caused by bugs in the program.
### The panic! macro
- When executed, the program will:
1. Print a failure message.
1. Unwind and clean up the stack.
1. Quit.
---
### Unwinding the stack or Aborting
- __*Unwinding*__ is the term used for *error backtrace*.
- It is the default behaviour.
- An alternative to unwinding is to __*abort*__ the program.
- The program quits without cleaning up the stack.
- For example, to choose the abort behaviour for the release profile, add the following directive to the __*Cargo.toml*__ file:
```rust=
[profile.release]
panic = 'abort'
```
---
### The panic! macro: Direct call
#### Example
```rust=
//src/main.rs
fn main() {
panic!("crash and burn");
}
```
#### Output
```bash=
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.25s
Running `target/debug/panic`
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note : run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```
---
### The panic! macro: Backtrace
#### Example
```rust=
//src/main.rs
fn main() {
let v = vec![1, 2, 3];
v[99];
}
```
#### Output
```bash=
$ cargo run
Compiling panic v0.1.0 (file:///projects/panic)
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/panic`
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/slice/mod.rs:2806:10
note : run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```
---
### The panic! macro: Backtrace
#### The RUST_BACKTRACE environment variable
※ Only works in debug mode
```bash=
$ RUST_BACKTRACE=1 cargo run
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/slice/mod.rs:2806:10
stack backtrace:
0: backtrace::backtrace::libunwind::trace
at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
1: backtrace::backtrace::trace_unsynchronized
at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
2: std::sys_common::backtrace::_print_fmt
at src/libstd/sys_common/backtrace.rs:84
3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
at src/libstd/sys_common/backtrace.rs:61
4: core::fmt::ArgumentV1::show_usize
5: std::io::Write::write_fmt
at src/libstd/io/mod.rs:1426
6: std::sys_common::backtrace::_print
at src/libstd/sys_common/backtrace.rs:65
7: std::sys_common::backtrace::print
at src/libstd/sys_common/backtrace.rs:50
8: std::panicking::default_hook::{{closure}}
at src/libstd/panicking.rs:193
9: std::panicking::default_hook
at src/libstd/panicking.rs:210
10: std::panicking::rust_panic_with_hook
at src/libstd/panicking.rs:471
11: rust_begin_unwind
at src/libstd/panicking.rs:375
12: core::panicking::panic_fmt
at src/libcore/panicking.rs:84
13: core::panicking::panic_bounds_check
at src/libcore/panicking.rs:62
14: <usize as core::slice::SliceIndex<[T]>>::index
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/slice/mod.rs:2806
15: core::slice::<impl core::ops::index::Index<I> for [T]>::index
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libcore/slice/mod.rs:2657
16: <alloc::vec::Vec<T> as core::ops::index::Index<I>>::index
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/liballoc/vec.rs:1871
17: panic::main
at src/main.rs:4
18: std::rt::lang_start::{{closure}}
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
19: std::rt::lang_start_internal::{{closure}}
at src/libstd/rt.rs:52
20: std::panicking::try::do_call
at src/libstd/panicking.rs:292
21: __rust_maybe_catch_panic
at src/libpanic_unwind/lib.rs:78
22: std::panicking::try
at src/libstd/panicking.rs:270
23: std::panic::catch_unwind
at src/libstd/panic.rs:394
24: std::rt::lang_start_internal
at src/libstd/rt.rs:51
25: std::rt::lang_start
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
26: panic::main
```
---
## Recoverable errors
### What are recoverable errors
- Less serious errors that do not need to stop the program execution.
- Easy to understand and fix.
- For example, trying to opening a non-existent file.
### The Result enum
```rust=
enum Result<T, E> {
Ok(T), // variant for success; T type returned for success
Err(E), // variant for failure; E type of error returned for failure
}
```
---
### The Result enum
#### Example
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
}
```
#### Introducing the error by using a wrong annotation type
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f: u32 = File::open("hello.txt");
}
```
---
### The Result enum
#### Compile attempt
```bash=
$ cargo run
Compiling error-handling v0.1.0 (file:///projects/error-handling)
error[E0308]: mismatched types
--> src/main.rs:4:18
|
4 | let f: u32 = File::open("hello.txt");
| --- ^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found enum `std::result::Result`
| |
| expected due to this
|
= note: expected type `u32`
found enum `std::result::Result<std::fs::File, std::io::Error>`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
error: could not compile `error-handling`.
To learn more, run the command again with --verbose.
```
---
### Using the match control flow operator
#### Example
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
```
```bash=
$ cargo run
Compiling error-handling v0.1.0 (file:///projects/error-handling)
Finished dev [unoptimized + debuginfo] target(s) in 0.73s
Running `target/debug/error-handling`
thread 'main' panicked at 'Problem opening the file: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:8:23
note : run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```
---
### Matching on different errors
#### Example
```rust=
//src/main.rs
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
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)
}
},
};
}
```
---
### Shortcuts on error: Unwrap and Expect
#### Example (unwrap)
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f = File::open("hello.txt").unwrap();
}
```
#### Output
```bash=
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {
repr: Os { code: 2, message: "No such file or directory" } }',
src/libcore/result.rs:906:4
```
---
### Shortcuts on error: Unwrap and Expect
#### Example (expect)
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f = File::open("hello.txt").expect("Failed to open hello.txt");
}
```
#### Output
```bash=
thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:
2, message: "No such file or directory" } }', src/libcore/result.rs:906:4
```
---
### Propagating errors
#### Example
```rust=
//src/main.rs
use std::fs::File;
use std::io;
use std::io::Read;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
```
---
### Propagating errors
#### The *?* operator
```rust=
//src/main.rs
use std::fs::File;
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)
}
```
---
### Propagating errors
#### The *?* operator
```rust=
//src/main.rs
use std::fs::File;
use std::io;
use std::io::Read;
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
```
```rust=
//src/main.rs
use std::fs;
use std::io;
fn read_username_from_file() -> Result<String, io::Error> {
fs::read_to_string("hello.txt")
}
```
---
### Propagating errors
#### *?* Used in the *main* function
```rust=
//src/main.rs
use std::fs::File;
fn main() {
let f = File::open("hello.txt")?;
}
```
---
### Propagating errors
#### *?* Used in the *main* function
```bash=
$ cargo run
Compiling error-handling v0.1.0 (file:///projects/error-handling)
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:4:13
|
3 | / fn main() {
4 | | let f = File::open("hello.txt")?;
| | ^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
5 | | }
| |_- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `error-handling`.
To learn more, run the command again with --verbose.
```
---
### Propagating errors
#### *?* Used in functions that return Result
```rust=
//src/main.rs
use std::error::Error;
use std::fs::File;
fn main() -> Result<(), Box<dyn Error>> {
let f = File::open("hello.txt")?;
Ok(())
}
```
---
## When to use panic! or Result
### Decide how to handle an error
- Call panic! for any error occurrence.
- There is no way to recover from such situation.
- Return a Result to the calling code.
- It is possible to recover from the error situation.
---
### Decide how to handle an error
#### Use unwrap and expect during prototyping
- Quick and easy to handle in debug.
- Actual error handling implemented in release.
---
### Decide how to handle an error
#### Use unwrap to handle Ok value cases
```rust=
//src/main.rs
fn main() {
use std::net::IpAddr;
let home: IpAddr = "127.0.0.1".parse().unwrap();
}
```
---
### Decide how to handle an error
#### General guidelines
- When to use panic!
- The failure is expected to happen occasionally.
- The code might end up in a *bad* state that would compromise the rest of the execution.
- There is no other better way to handle the error.
- When to use Result
- The failure is expected to happen frequently.
- For example, upon data input by the user.
---
### Decide how to handle an error
#### Custom types for validation
Guessing Game snippet:
```rust=
loop {
// --snip--
let guess: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if guess < 1 || guess > 100 {
println!("The secret number will be between 1 and 100.");
continue;
}
match guess.cmp(&secret_number) {
// --snip--
}
```
---
### Decide how to handle an error
#### Custom types for validation
```rust=
pub struct Guess {
value: i32,
}
impl Guess {
pub fn new(value: i32) -> Guess {
if value < 1 || value > 100 {
panic!("Guess value must be between 1 and 100, got {}.", value);
}
Guess { value }
}
pub fn value(&self) -> i32 {
self.value
}
}
```
<div style="text-align: right">□</div>
---
## References
From __The Rust Programming Language__ book:
- Ch. 09: Error Handling [[EN]](https://doc.rust-lang.org/book/ch09-00-error-handling.html) [[JP]](https://doc.rust-jp.rs/book-ja/ch09-00-error-handling.html)
---
---
### owacchatta!!1!
{"metaMigratedAt":"2023-06-15T13:12:28.563Z","metaMigratedFrom":"YAML","title":"Rust Ch.9 - Error Handling","breaks":true,"description":"Rust Ch.9 - Error Handling","slideOptions":"{\"theme\":\"black\",\"slideNumber\":\"c/t\",\"center\":false,\"transition\":\"fade\",\"keyboard\":true}","contributors":"[{\"id\":\"c5f0d40d-be35-4660-a8b4-7736feeb9327\",\"add\":22478,\"del\":6693}]"}