CodeBlanks Logo Icon CodeBlanks

thiserror

Build derived error types with the thiserror crate.

thiserror

In libraries, error types are usually an enum, and writing Display for each variant is repetitive. The thiserror crate generates it with a derive:

// Cargo.toml: thiserror = "1"
use thiserror::Error;

#[derive(Debug, Error)]
enum AyarHatasi {
    #[error("eksik anahtar: {0}")]
    Eksik(String),
    #[error("geçersiz değer: {anahtar}={deger}")]
    Gecersiz { anahtar: String, deger: String },
}

The code it generates is identical to the impl Display and impl Error you would write by hand; there's no runtime cost.

The hand-written equivalent

Since there are no external crates in the exercise environment, we'll write the same type by hand. This is what's behind thiserror:

impl fmt::Display for AyarHatasi {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AyarHatasi::Eksik(a) => write!(f, "eksik anahtar: {a}"),
            AyarHatasi::Gecersiz { anahtar, deger } => {
                write!(f, "geçersiz değer: {anahtar}={deger}")
            }
        }
    }
}

impl std::error::Error for AyarHatasi {}

Practice Settings errors

Define the AyarHatasi enum with the variants Eksik(String) and Gecersiz { anahtar, deger }; implement Display and Error by hand. Print Eksik("port") and Gecersiz { anahtar: "port", deger: "abc" } with {} on separate lines.

Starter code

main.rs
use std::fmt;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use std::fmt;

#[derive(Debug)]
enum AyarHatasi {
    Eksik(String),
    Gecersiz { anahtar: String, deger: String },
}

impl fmt::Display for AyarHatasi {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AyarHatasi::Eksik(a) => write!(f, "eksik anahtar: {a}"),
            AyarHatasi::Gecersiz { anahtar, deger } => {
                write!(f, "geçersiz değer: {anahtar}={deger}")
            }
        }
    }
}

impl std::error::Error for AyarHatasi {}

fn main() {
    let a = AyarHatasi::Eksik(String::from("port"));
    let b = AyarHatasi::Gecersiz {
        anahtar: String::from("port"),
        deger: String::from("abc"),
    };
    println!("{}", a);
    println!("{}", b);
}

Expected output

eksik anahtar: port
geçersiz değer: port=abc

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 <code>thiserror</code> or <code>anyhow</code>? What is the general rule?

  • thiserror everywhere
  • Both do the same thing
  • thiserror in libraries (concrete types), anyhow in applications (one flexible type) Correct answer
  • anyhow everywhere

Explanation: A library user wants to handle errors by kind; an application mostly reports the error.

2 Message attribute

Question
#[derive(Debug, Error)]
enum AyarHatasi {
    #[____("eksik anahtar: {0}")]
    Eksik(String),
}
Answer
#[derive(Debug, Error)]
enum AyarHatasi {
    #[error("eksik anahtar: {0}")]
    Eksik(String),
}

Explanation: thiserror takes each variant's message from #[error(..)].

3 Value and message

Term Meaning
Eksik("port") eksik anahtar: port
Gecersiz { port, abc } geçersiz değer: port=abc
#[error("{0}")] First field
#[error(transparent)] Inner error's message

All lessons in Intermediate