Table of contents

Introduction

If you’ve used the thiserror crate, you’ll probably have seen string interpolation used on the #error(...) annotation. At runtime, the error message is interpolated with the values from the enum.

#[derive(thiserorr::Error)
enum AppError<'a> {
	#[error("Internal server error")
	InternalServerError,

	#[error("Document not found: {0}")]
	DocumentNotFound(DocumentId),

	#[error("Invalid query: {name} && {meta_id}")]
	InvalidAndQuery {
		name: &'a str,
		meta_id: MetaId,
	}
}

Goal

It begs the question of how the internal workings of the Display trait are implemented, from extracting the corresponding fields to correctly interpolating the values at runtime whenever Display::fmt(...) is called.

To understand the behaviour, we’ll attempt implementing a custom but much simpler implementation of the thiserror::Error crate, except that we’ll proceed to call ours SimpleError.

#[derive(thiserror::Error, SimpleError)
enum AppError {
	#[error("Document not found: {0}")]
	DocumentNotFound(DocumentId),
}

Deep Dive

Parsing the message

Our first focus will be to extract the literal string within the #[error(....)] annotation and keep it in a way we can easily interpolate in the Display trait. To achieve the latter goal, we’ll want to do the following for the string literal we extract in our derive macro: