<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}]"}
    655 views