Multiple Error Types

Note: No official YouTube video yet, but our Rust Developer Bootcamp covers this topic!

No video yet...

Exercises

Error trait object

// Make the code compile by addressing the TODO.

use std::error;
use std::fmt;
use std::num::ParseIntError;

// TODO: update the return type of `main()` to make this compile.
fn main() -> Result<(), Box<dyn ???>> {
    let pretend_user_input = "42";
    let x: i64 = pretend_user_input.parse()?;
    println!("output={:?}", PositiveNonzeroInteger::new(x)?);
    Ok(())
}

// Don't change anything below this line.

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        match value {
            x if x < 0 => Err(CreationError::Negative),
            x if x == 0 => Err(CreationError::Zero),
            x => Ok(PositiveNonzeroInteger(x as u64)),
        }
    }
}

// This is required so that `CreationError` can implement `error::Error`.
impl fmt::Display for CreationError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let description = match *self {
            CreationError::Negative => "number is negative",
            CreationError::Zero => "number is zero",
        };
        f.write_str(description)
    }
}

impl error::Error for CreationError {}
Solution
use std::error;
use std::fmt;
use std::num::ParseIntError;

fn main() -> Result<(), Box<dyn error::Error>> {
  let pretend_user_input = "42";
  let x: i64 = pretend_user_input.parse()?;
  println!("output={:?}", PositiveNonzeroInteger::new(x)?);
  Ok(())
}

// Don't change anything below this line.

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

#[derive(PartialEq, Debug)]
enum CreationError {
  Negative,
  Zero,
}

impl PositiveNonzeroInteger {
  fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
      match value {
          x if x < 0 => Err(CreationError::Negative),
          x if x == 0 => Err(CreationError::Zero),
          x => Ok(PositiveNonzeroInteger(x as u64)),
      }
  }
}

// This is required so that `CreationError` can implement `error::Error`.
impl fmt::Display for CreationError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      let description = match *self {
          CreationError::Negative => "number is negative",
          CreationError::Zero => "number is zero",
      };
      f.write_str(description)
  }
}

impl error::Error for CreationError {}

Mutiple errors 1

// Make the code compile by completing the add function.

use std::num::ParseIntError;

enum AddError {
    ParseError(ParseIntError),
    OverFlow,
}

// parse input strings to u8's and return their sum
fn add(num1: &str, num2: &str) -> Result {
    let num1 = num1.parse::<u8>().map_err(|e| AddError::ParseError(e))?;
    let num2 = num2.parse::<u8>().map_err(|e| AddError::ParseError(e))?;
    // ok_or() transforms Option<T> -> Result<T,E> and takes value of type E as input
    let sum = num1.checked_add(num2).ok_or()?;
    Ok(sum)
}

fn main() {
    let (user_input1, user_input2) = ("23", "45");
    match add(user_input1, user_input2) {
        Ok(sum) => println!("Sum = {sum}"),
        Err(e) => match e {
            AddError::OverFlow => println!("Sum > {}", u8::MAX),
            AddError::ParseError(e) => println!("Invalid input, parse error: {e:?}"),
        },
    }
}
Solution
use std::num::ParseIntError;

enum AddError {
  ParseError(ParseIntError),
  OverFlow,
}

// parse input strings to u8's and return their sum
fn add(num1: &str, num2: &str) -> Result<u8, AddError> {
  let num1 = num1.parse::<u8>().map_err(|e| AddError::ParseError(e))?;
  let num2 = num2.parse::<u8>().map_err(|e| AddError::ParseError(e))?;
  // ok_or() transforms Option<T> -> Result<T,E> and takes value of type E as input
  let sum = num1.checked_add(num2).ok_or(AddError::OverFlow)?;
  Ok(sum)
}

fn main() {
  let (user_input1, user_input2) = ("23", "45");
  match add(user_input1, user_input2) {
      Ok(sum) => println!("Sum = {sum}"),
      Err(e) => match e {
          AddError::OverFlow => println!("Sum > {}", u8::MAX),
          AddError::ParseError(e) => println!("Invalid input, parse error: {e:?}"),
      },
  }
}

Multiple errors 2

// Complete the code by addressing the TODOs.

use std::num::ParseIntError;

// This is a custom error type that we will be using in `parse_pos_nonzero()`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
    Creation(CreationError),
    ParseInt(ParseIntError),
}

impl ParsePosNonzeroError {
    fn from_creation(err: CreationError) -> ParsePosNonzeroError {
        ParsePosNonzeroError::Creation(err)
    }
    // TODO: add another error conversion function here.
    // fn from_parseint...
}

fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> {
    // TODO: change this to return an appropriate error instead of panicking
    // when `parse()` returns an error.
    let x: i64 = s.parse().unwrap();
    PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
}

// Don't change anything below this line.

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

#[derive(PartialEq, Debug)]
enum CreationError {
    Negative,
    Zero,
}

impl PositiveNonzeroInteger {
    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
        match value {
            x if x < 0 => Err(CreationError::Negative),
            x if x == 0 => Err(CreationError::Zero),
            x => Ok(PositiveNonzeroInteger(x as u64)),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parse_error() {
        // We can't construct a ParseIntError, so we have to pattern match.
        assert!(matches!(
            parse_pos_nonzero("not a number"),
            Err(ParsePosNonzeroError::ParseInt(_))
        ));
    }

    #[test]
    fn test_negative() {
        assert_eq!(
            parse_pos_nonzero("-555"),
            Err(ParsePosNonzeroError::Creation(CreationError::Negative))
        );
    }

    #[test]
    fn test_zero() {
        assert_eq!(
            parse_pos_nonzero("0"),
            Err(ParsePosNonzeroError::Creation(CreationError::Zero))
        );
    }

    #[test]
    fn test_positive() {
        let x = PositiveNonzeroInteger::new(42);
        assert!(x.is_ok());
        assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
    }
}
Solution
#![allow(unused)]
fn main() {
use std::num::ParseIntError;

// This is a custom error type that we will be using in `parse_pos_nonzero()`.
#[derive(PartialEq, Debug)]
enum ParsePosNonzeroError {
  Creation(CreationError),
  ParseInt(ParseIntError),
}

impl ParsePosNonzeroError {
  fn from_creation(err: CreationError) -> ParsePosNonzeroError {
      ParsePosNonzeroError::Creation(err)
  }
  fn from_parseint(err: ParseIntError) -> ParsePosNonzeroError {
      ParsePosNonzeroError::ParseInt(err)
  }
}

fn parse_pos_nonzero(s: &str) -> Result<PositiveNonzeroInteger, ParsePosNonzeroError> {
  let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parseint)?;
  PositiveNonzeroInteger::new(x).map_err(ParsePosNonzeroError::from_creation)
}

// Don't change anything below this line.

#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);

#[derive(PartialEq, Debug)]
enum CreationError {
  Negative,
  Zero,
}

impl PositiveNonzeroInteger {
  fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
      match value {
          x if x < 0 => Err(CreationError::Negative),
          x if x == 0 => Err(CreationError::Zero),
          x => Ok(PositiveNonzeroInteger(x as u64)),
      }
  }
}

#[cfg(test)]
mod test {
  use super::*;

  #[test]
  fn test_parse_error() {
      // We can't construct a ParseIntError, so we have to pattern match.
      assert!(matches!(
          parse_pos_nonzero("not a number"),
          Err(ParsePosNonzeroError::ParseInt(_))
      ));
  }

  #[test]
  fn test_negative() {
      assert_eq!(
          parse_pos_nonzero("-555"),
          Err(ParsePosNonzeroError::Creation(CreationError::Negative))
      );
  }

  #[test]
  fn test_zero() {
      assert_eq!(
          parse_pos_nonzero("0"),
          Err(ParsePosNonzeroError::Creation(CreationError::Zero))
      );
  }

  #[test]
  fn test_positive() {
      let x = PositiveNonzeroInteger::new(42);
      assert!(x.is_ok());
      assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap()));
  }
}
}