Table of contents
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,
}
}
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),
}
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:
Prefix all of the positional slots with an index, {<number>}
with __
Determine the index for positional slots without an index,{}
, and prefix it with __
Keep a sorted list of each identifier.
🕹 Expand to see code