Rust errors: Difference between revisions
No edit summary |
No edit summary |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
Rust has two primary methods of handling errors. | Rust has two primary methods of handling errors. | ||
* <code>panic!()</code> halts/exits the program | * <code>panic!()</code> halts/exits the program | ||
* <code>Result</code> types are for handle-able errors | * <code>Result</code> types are for handle-able errors (which frequently pass <code>std::error::Error</code> objects) | ||
= panic = | = panic = | ||
Line 52: | Line 52: | ||
{{ expand | {{ expand | ||
| <code>match</code> | | <code>match</code> Ok/Err (optionally break/return/panic on Err) | ||
| | | | ||
Line 65: | Line 65: | ||
{{ expand | {{ expand | ||
| | | Propagate if Err() | ||
| | | | ||
It's also fairly common that you want to delegate handling the error to the caller.<br> | It's also fairly common that you want to delegate handling the error to the caller.<br> | ||
Line 97: | Line 82: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | |||
{{ expand | |||
| Common Result Methods | |||
| | |||
Confirm status | |||
<syntaxhighlight lang="rust"> | |||
.is_ok() // true if is Ok-val | |||
.is_err() // true if is Err-val | |||
</syntaxhighlight> | |||
Expected Result, or Panic w/ Message | |||
<syntaxhighlight lang="rust"> | |||
.expect("error because..") // panic with this mssage if Err(), otherwise returns Ok-val | |||
.expect_err("error because..") // panic with this message if Ok(), otherwise return Err-val | |||
</syntaxhighlight> | |||
Unwrap (to raw val) | |||
<syntaxhighlight lang="rust"> | |||
// panic variations | |||
.unwrap() // Ok-val or panic! | |||
.unwrap_err() // Err-val or panic! | |||
// non-panic variations | |||
.into_ok_or_err() // return Ok-val if Ok(), Err-val if Err() | |||
.unwrap_or(456) // (eager) Ok-val, w/ fallback on default | |||
.unwrap_or_else(|_| 1 + 1) // (lazy) Ok-val, w/ fallback on default, calculated in closure | |||
.unwrap_or_default() // Ok-val, w/ fallback on Ok-val-type's default | |||
</syntaxhighlight> | |||
Map (transform Ok/Err vals) | |||
<syntaxhighlight lang="rust"> | |||
.map(|v| v.to_string()) // Mutate the Ok-vals, preserve Err-vals, return Result | |||
.map_err(|e| e.as_str()) // Mutate the Err-vals, preserve Ok-vals, return Result | |||
</syntaxhighlight> | |||
Or (try changing Err to Ok) | |||
<syntaxhighlight lang="rust"> | |||
// (eager) If Err-val, use closure to try changing it to Ok-val | |||
// (also accepts Err-val), return Result | |||
.or(|r| Ok(r.unrap() * 2)) | |||
// (lazy) If Err-val, use closure to try changing it to Ok-val | |||
// (also accepts Err-val), return Result | |||
.or_else(|r| Ok(r.unwrap() * 2)) | |||
</syntaxhighlight> | |||
Convert to Option | |||
<syntaxhighlight lang="rust"> | |||
.ok() // convert to Option (Ok->Some, Err->None) | |||
.err() // convert to Option (Ok->None, Err->Some) | |||
</syntaxhighlight> | |||
Misc | |||
<syntaxhighlight lang="rust"> | |||
.iter() // (iterate over contained value? if ok-val is array?) | |||
.contains(2) // return true if item is Ok(), and Ok-val matches | |||
</syntaxhighlight> | |||
}} | }} |
Latest revision as of 00:04, 10 February 2023
Rust has two primary methods of handling errors.
panic!()
halts/exits the programResult
types are for handle-able errors (which frequently passstd::error::Error
objects)
panic
- intended for halting application, not control flow
- have backtraces
panic!("tried to X but couldn't Y") // convert panic to result // (not intended for native rust code) let result = panic::catch_unwind(|| { panic!("oh no!"); });
Result
The result type is an enum, whose options
Ok, Err
have been merged into the global scope throughprelude
.
Err
can be any type, but the standard library predominantly usesstd::error::Error
concretions.Given this Result producing code:
use std::error::Error; use std::fmt; // A custom std::error::Error impl #[derive(Debug)] struct MyError {} impl Error for MyError{} impl fmt::Display for MyError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "i borked because...") } } // a result type fn is_one(i: isize) -> Result<String, MyError> { if i == 1 { Ok("success".to_string()) } else { Err(MyError{}) } }
match
Ok/Err (optionally break/return/panic on Err)
let result = match is_one(2) { Ok(x) => format!("horay: {}", x), Err(_) => panic!("an error occurred") }; println!("{}", result);Propagate if Err()
It's also fairly common that you want to delegate handling the error to the caller.
There is also syntactic sugar for this.The
?
operator returns with theErr
if it errored.
You'll only need to provide theOk()
value.
(note you can also pass an Option and receive an Option in the same manner)fn print_if_result_ok(i: isize) -> Result<String, MyError> { let result = is_one(i)? // return Err() if err println!("success value: {}", result); // unwraps result, and you know it is Ok() now Ok("success") }
Common Result Methods
Confirm status.is_ok() // true if is Ok-val .is_err() // true if is Err-valExpected Result, or Panic w/ Message
.expect("error because..") // panic with this mssage if Err(), otherwise returns Ok-val .expect_err("error because..") // panic with this message if Ok(), otherwise return Err-valUnwrap (to raw val)
// panic variations .unwrap() // Ok-val or panic! .unwrap_err() // Err-val or panic! // non-panic variations .into_ok_or_err() // return Ok-val if Ok(), Err-val if Err() .unwrap_or(456) // (eager) Ok-val, w/ fallback on default .unwrap_or_else(|_| 1 + 1) // (lazy) Ok-val, w/ fallback on default, calculated in closure .unwrap_or_default() // Ok-val, w/ fallback on Ok-val-type's defaultMap (transform Ok/Err vals)
.map(|v| v.to_string()) // Mutate the Ok-vals, preserve Err-vals, return Result .map_err(|e| e.as_str()) // Mutate the Err-vals, preserve Ok-vals, return ResultOr (try changing Err to Ok)
// (eager) If Err-val, use closure to try changing it to Ok-val // (also accepts Err-val), return Result .or(|r| Ok(r.unrap() * 2)) // (lazy) If Err-val, use closure to try changing it to Ok-val // (also accepts Err-val), return Result .or_else(|r| Ok(r.unwrap() * 2))Convert to Option
.ok() // convert to Option (Ok->Some, Err->None) .err() // convert to Option (Ok->None, Err->Some)Misc
.iter() // (iterate over contained value? if ok-val is array?) .contains(2) // return true if item is Ok(), and Ok-val matches
std::error::Error
The standard library predominantly uses
std::error::Error
concretions asErr
within Result objects.
It's particularly useful to implement errors as enums, to enumerate the various errors it can handle.Error implementors must:
- be derived from Debug
- implement Display
use std::error::Error; use std::fmt; #[derive(Debug)] pub enum MyError { FileNotAccessible, FileIsLocked, } impl Error for MyError{} impl fmt::Display for MyError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { MyError::FileNotAccessible => write!(f, "file not accessible.."), MyError::FileIsLocked => write!(f, "file is locked.."), } } }