Consider the following code:
enum MyError {
Io(io::Error),
Parser(num::ParseIntError)
}
use crate::MyError::{Io, Parser};
impl From<std::io::Error> for MyError {
fn from(err: std::io::Error) -> Self {
Io(err)
}
}
impl From<num::ParseIntError> for MyError {
fn from(err: num::ParseIntError) -> Self {
Parser(err)
}
}
fn get_file(s: &str) -> Result<File, io::Error> {
let file = File::open("sdasfsadas")?;
Ok(file)
}
fn parse_int(file: &File) -> Result<u32, num::ParseIntError> {
Ok(2u32)
}
fn handle_file(s: &str) -> Result<u32, MyError> {
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}
As we can see there are lots of boilerplate code !!
Let's simplify it:
/// Auto generated code
/// enum MyError {
/// Io(io::Error),
/// Parser(num::ParseIntError)
/// }
///
/// use crate::MyError::{Io, Parser};
///
/// impl From<std::io::Error> for MyError {
/// fn from(err: std::io::Error) -> Self {
/// Io(err)
/// }
/// }
///
/// impl From<num::ParseIntError> for MyError {
/// fn from(err: num::ParseIntError) -> Self {
/// Parser(err)
/// }
/// }
fn get_file(s: &str) -> File throws { // Auto generated result Result<u32, io::Error>
let file = File::open("sdasfsadas")?;
Ok(file)
}
fn parse_int(file: &File) -> u32 throws num::ParseIntError { // Auto generated result Result<u32, num::ParseIntError>
Ok(2u32)
}
fn handle_file(s: &str) -> u32 throws { // Auto generated result Result<u32, MyError> and enum MyError
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}
It is possible also to specify errors manually:
fn get_file(s: &str) -> File throws io::Error { // Auto generated result Result<u32, io::Error>
let file = File::open("sdasfsadas")?;
Ok(file)
}
fn parse_int(file: &File) -> u32 throws num::ParseIntError { // Auto generated result Result<u32, num::ParseIntError>
Ok(2u32)
}
fn handle_file(s: &str) -> u32 throws io::Error, num::ParseIntError { // Auto generated result Result<u32, MyError> and enum MyError
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}
When user specify one error after throws we should specify all errors
Also we could specify our own custom enum error MyError after throws keyword:
fn handle_file(s: &str) -> u32 throws MyError { // Auto generated result Result<u32, MyError>
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}
In this case we should specify MyError manually as previously
Okay, let's look how the caller will handle result of handle_file
:
fn handle_file(s: &str) -> u32 throws MyError { // Auto generated result Result<u32, MyError>
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}
fn main() {
let res = handle_file("some.txt");
match res {
Ok(res_code) => println!("res_code is {}", res_code),
Err(IoError(err)) => println!("io_err is {}", err),
Err(NumParserIntError(err)) => println!("parse_err is {}", err),
};
}
This topic relate to:
It is also possible to handle multiple errors without introducing a new keyword and syntax, but instead by adding anonymous enum types:
fn get_file(s: &str) -> Result<u32, io::Error> {
let file = File::open("sdasfsadas")?;
Ok(file)
}
fn parse_int(file: &File) -> Result<u32, num::ParseIntError> {
Ok(2u32)
}
fn handle_file(s: &str) -> Result<u32, io::Error | num::ParseIntError> { // Where io::Error | num::ParseIntError is auto generated anonymuos enum AnonymousEnumError
let file = get_file(s)?;
let int = parse_int(&file)?;
Ok(int)
}