Hand-writing Display, Error, and From for every error variant is real boilerplate. Two crates own this space, and which you choose says something about what you're building.
thiserror — for libraries
thiserror generates the boilerplate for a concrete error enum. You write the variants and annotate them with #[error("...")]; the macro derives Display, Error, and (with #[from]) the From conversions. The result is a precise, matchable error type — exactly what a library should expose so its callers can react to specific failures.
anyhow — for applications
anyhow gives you one catch-all type, anyhow::Error, that any error converts into. You stop naming error types and just ? everything, adding context with .context("while loading config"). It's perfect for application code — a CLI, a binary — where you don't need callers to match on error kinds; you just want a good message and a backtrace.
match on your errors, give them a real enum via thiserror. If you're at the top of the stack and just want failures to propagate with context, use anyhow. Many real projects use both — thiserror in the library crates, anyhow in the binary.You've earned these
Notice what these crates are: macro-generated trait implementations. thiserror derives From and Display for you; anyhow leans on Box<dyn Error> and the ? conversion you already understand. They're not magic — they're the patterns from this track, automated. That's the Rust ecosystem in miniature: a small crate that removes boilerplate around a language feature you already know.