Guide

Custom errors

Real programs fail in their own ways. You can make your own error type so callers know exactly what went wrong.

Error enums

An enum lists every way an operation can fail. The caller can match on it.

#[derive(Debug)]enum PinError {    TooShort,    NotDigits,}fn check(pin: &str) -> Result<(), PinError> {    if pin.len() < 4 {        return Err(PinError::TooShort);    }    Ok(())}

Display for errors

#[derive(Debug)] gives a message for developers, like TooShort. For a readable message, implement Display. Then {} prints your text. Inside fmt, use write!(f, ...), not println!.

use std::fmt;impl fmt::Display for PinError {    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {        match self {            PinError::TooShort => write!(f, "PIN is too short"),            PinError::NotDigits => write!(f, "PIN must be digits"),        }    }}

From and ?

? can change one error type into another. It does this with From. If you write impl From<ParseIntError> for AppError, then ? on a parse result works in a function that returns Result<_, AppError>. Without it, s.parse::<i32>()? fails with E0277. With let n: i32 = s.parse()?; the code is E0271 instead, but it is the same problem.

use std::num::ParseIntError;impl From<ParseIntError> for AppError {    fn from(_: ParseIntError) -> Self {        AppError::BadNumber    }}

Box<dyn Error>

Box<dyn std::error::Error> can hold any error type. It is handy when a function can fail in several ways and you do not need a custom enum.

use std::error::Error;fn main() -> Result<(), Box<dyn Error>> {    let n: i32 = "7".parse()?;    let x: f64 = "0.5".parse()?;    println!("{}", n as f64 + x);    Ok(())}

Remember

  • An error enum names each way to fail.
  • Display gives an error a readable message.
  • impl From lets ? convert errors for you.
  • Box<dyn Error> holds any error, so ? works on many kinds.