RustArena

Step 2 of 7

Display: messages for people

{:?} prints InsufficientFunds { balance: 70, requested: 500 }. That's useful in a log for developers, but a customer should see insufficient funds: balance 70, requested 500. The {} placeholder uses a different format, called Display, and for your own types you write it yourself (you did it for Money in module 18). Error types work the same way.

Display is a trait: a named set of methods that a type promises to have (module 22 covers traits properly). You make {} work for your type by implementing the Display trait for it. The recipe always has the same shape:

use std::fmt;

#[derive(Debug)]
enum WithdrawError {
    InvalidAmount,
    InsufficientFunds { balance: u64, requested: u64 },
    AccountFrozen,
}

impl fmt::Display for WithdrawError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            WithdrawError::InvalidAmount => write!(f, "amount must be greater than zero"),
            WithdrawError::InsufficientFunds { balance, requested } => {
                write!(f, "insufficient funds: balance {balance}, requested {requested}")
            }
            WithdrawError::AccountFrozen => write!(f, "account is frozen"),
        }
    }
}

fn main() {
    let errors = [
        WithdrawError::InvalidAmount,
        WithdrawError::InsufficientFunds { balance: 70, requested: 500 },
        WithdrawError::AccountFrozen,
    ];
    for e in &errors {
        println!("declined: {e}");
    }
    let message = errors[2].to_string();
    println!("{message:?} has {} characters", message.len());
    println!("{:?}", errors[0]);
}
declined: amount must be greater than zero
declined: insufficient funds: balance 70, requested 500
declined: account is frozen
"account is frozen" has 17 characters
InvalidAmount

Piece by piece:

Workplace note: the standard library's documentation for errors recommends short, lowercase messages without a trailing period, like its own invalid digit found in string. Messages often get embedded in longer ones (error: could not load config: invalid digit found in string), and that reads well only if each piece is a lowercase fragment.

Common mistakes

#[derive(Debug)]
enum WithdrawError {
    AccountFrozen,
}

fn main() {
    let e = WithdrawError::AccountFrozen;
    println!("declined: {e}");
}
error[E0277]: `WithdrawError` doesn't implement `std::fmt::Display`

Your task

Implement Display for TicketError so that:

Challenge 2

Give UsernameError (from the last step) a Display implementation:

Challenge 3

Write a grading function with its own error type, from scratch.

Previous: An enum for everything that can go wrong Next: The Error trait and Box<dyn Error>