Error Handling
Put the Result value of read_line into a variable, then match on the enum and run logic depedning on which one it is.
#![allow(unused)] fn main() { let stdin_result = std::io::stdin().read_line(&mut guess); match stdin_result { Ok(..) => println!("Cool one!"), Err(..) => println!("Oh no!"), } }
Shortcut methods
unwrap
return value or call panic if errorexpect
return value or panic with custom message if error
Match on error kind / type
#![allow(unused)] 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) } }, }; println!("{:?}", f) }
More concise nested error with closure
#![allow(unused)] fn main() { let f = 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)) } else { panic!("Problem opening the file: {:?}", error) } }); }
Propogate Errors
use std::fs::File; use std::io; use std::io::Read; fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("helo.txt"); // If error returns error back to caller of method let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); // Because last call it returns <String, io::Error> match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } } fn main() { let f = read_username_from_file().expect("Panic with error from main"); println!("{:?}", f); }
? to propogate errors
This is the same as the previous example ? = Ok if all good and continue, return error to calling code if not
#![allow(unused)] fn main() { 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) } }
Even cleaner!
#![allow(unused)] fn main() { 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 has a function already defined for this
#![allow(unused)] fn main() { fs::read_to_string("hello.txt"); }
NOTE
? uses the from function (trait) to convert itself to a standard error type.
Return error from main
use std::error::Error; use std::fs::File; fn main() -> Result<(), Box<dyn Error>> { let f = File::open("hello.txt")?; println!("{:?}", f); Ok(()) }
Unwrap or else with closure
#![allow(unused)] fn main() { let config = Config::new(&args).unwrap_or_else(|err| { println!("Problem parsing arguments: {}", err); process::exit(1); }); }
Group errors together
So you can return a single error type and then check what specific error it was at the caller via match
or if let
#![allow(unused)] fn main() { enum ParsePosNonzeroError { Creation(CreationError), ParseInt(ParseIntError), } impl ParsePosNonzeroError { fn from_creation(err: CreationError) -> ParsePosNonzeroError { ParsePosNonzeroError::Creation(err) } fn from_parse_int(err: ParseIntError) -> ParsePosNonzeroError { ParsePosNonzeroError::ParseInt(err) } } }