Error Handling
Rust has a generic error interface: std::error::Error
, it's optionally may have a source error and/or backtrace. Instead of exceptions and try..catch the follow is used:
Kotlin
fun main() {
try {
openSocket()
println("Worked")
} catch (e: IOException) {
println("Error: " + e.message)
}
}
Rust
use std::error::Error as StdError; type Error = Box<dyn StdError>; fn main() { match open_socket() { Ok(()) => println!("Worked"), Err(e) => println!("Error: {}", e) } } fn open_socket() -> Result<(), Error> { Ok(()) }
You can call unwrap()
on a Result
to get the success value or crash the app. To avoid repetitive code you can use ?
which will immediately return the error:
fn unwrap_example() {
let foo = open().unwrap();
}
//the question mark operator is only valid in methods that return Result
fn better_example() -> Result<(), Error> {
let bar = open()?;
Ok(())
}
Some crates errors aren't compatible with each other and so have to be converted to something before the error can be returned:
fn main() -> Result<(), Error> {
let file = open_file()?; //Error type is IoError
let socket = open_socket()?; //Error type is NetError
Ok(())
}
This example wouldn't compile because the size in memory of a return type must be known at compile time and Error
is a trait (an interface
in Kotlin). So it must be wrapped like so Box<dyn Error>
, Box
moves the value to the heap and is essentially a pointer, dyn
just means the type is dynamically dispatched (i.e. a trait). (see https://doc.rust-lang.org/std/keyword.dyn.html)
- Create a wrapper type and implement it for all error types that you have to handle
- Use eyre
I highly recommend anyhow for all apps, it automatically handles all error types and reduces boilerplate:
use eyre::Result; //import eyre Result and Error instead of std as needed
fn main() -> Result<()> { //Result only has single parameter
Ok(())
}