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)

  1. Create a wrapper type and implement it for all error types that you have to handle
  2. 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(())
}