An error should name the operation that failed. When a lower layer caused it, the caller also needs a way to inspect that cause. Rust’s Error trait supports this with Display for the current error and source() for an underlying one.
Imagine a service reading its configuration. These messages are illustrative:
Task
could not start service
Configuration
could not readsettings.toml
I/O
permission denied
The task error says what stopped. The configuration error names the file. The I/O error gives the immediate reason. Each layer adds what it knows.
To build the middle link, store the I/O error in ConfigError. Its Display implementation describes the configuration failure. Its source() implementation returns the stored cause:
impl std::error::Error for ConfigError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.cause)
}
}
Here, cause is a field of type std::io::Error. A caller can follow source() to inspect it and, if present, the next cause. The method returns one link; showing the whole chain is the reporter’s job.
Rust’s documentation advises a wrapper to expose its inner error through source() or include it in its own Display. Choose one place for that lower-level message. When a report prints the chain, each line can then contribute a distinct piece of the diagnosis.

The Campfire
No commentsNobody has pulled up a log by this one yet. Be the first to say what you make of it.
Held for the desk. It appears after a look.