Error and Result¶
The Result alias¶
The default type parameter is the point: rtb_error::Result<()> means
std::result::Result<(), rtb_error::Error>, and rtb_error::Result<(),
MyError> still works when you want your own error type. It is a plain alias
with no behaviour of its own.
rtb-cli re-exports both under different names from its prelude
(pub use rtb_error::{Error as RtbError, Result as RtbResult};), so code
written against that framework sees RtbResult<T> for the same type.
Every variant of Error¶
Error is #[non_exhaustive] and derives Debug, thiserror::Error and
miette::Diagnostic.
| Variant | Payload | Display |
Diagnostic code | Help |
|---|---|---|---|---|
Config(String) |
the rejection message | configuration error: {0} |
rtb::config |
none |
Io(std::io::Error) |
the underlying I/O error | I/O error: {0} |
rtb::io |
none |
CommandNotFound(String) |
the name the user typed | command not found: {0} |
rtb::command_not_found |
run--helpto list available commands |
FeatureDisabled(&'static str) |
the Cargo feature name | feature `{0}` is not compiled in |
rtb::feature_disabled |
rebuild with the appropriate Cargo feature enabled |
Other(Box<dyn Diagnostic + Send + Sync + 'static>) |
a downstream diagnostic | whatever the inner error displays | the inner diagnostic's code | the inner diagnostic's help |
Four of the five variants carry a code in the rtb:: namespace. Other does
not: it is declared #[diagnostic(transparent)], so every diagnostic facet —
code, help, severity, URL, labels, source code, related diagnostics — is read
straight off the boxed error. A rendered Error::Other never mentions
rtb::other, and there is a test asserting exactly that.
Error::Other prints its message twice¶
Other is declared #[error("{0}")] Other(#[from] Box<dyn Diagnostic + …>).
The #[from] also makes the boxed error the variant's source, so the
graphical renderer prints the message once as the diagnostic and once as its
cause:
It is cosmetic and there is no way to suppress it from the outside. The
simplest avoidance is not to box: a Diagnostic converts into a
miette::Report directly, so most code never needs Error::Other at all. See
Define your own error type.
What converts into Error automatically¶
Only two From impls exist, both generated by #[from]:
impl From<std::io::Error> for Error // → Error::Io
impl From<Box<dyn Diagnostic + Send + Sync + 'static>> for Error // → Error::Other
So ? promotes an std::io::Error for free:
fn read(path: &Path) -> rtb_error::Result<String> {
Ok(std::fs::read_to_string(path)?) // → Error::Io
}
Your own error type does not convert automatically, even when it implements
Diagnostic. Box it at the boundary:
Box::new(e) is coerced to Box<dyn Diagnostic + Send + Sync + 'static> at
the call site, so no turbofish is needed — but the error type must be Send +
Sync + 'static or the coercion fails to compile.
Why matching on Error needs a wildcard arm¶
#[non_exhaustive] means a downstream match must always end with _ =>.
Without one the compiler rejects it:
error[E0004]: non-exhaustive patterns: `_` not covered
= note: `rtb_error::Error` is marked as non-exhaustive, so a wildcard `_`
is necessary to match exhaustively
That is the intended behaviour, not a bug to work around: it is what lets a new variant ship in a minor release without breaking you.
What Error is not¶
Error is the errors the application scaffolding raises — a config source
rejecting a value, a command name that does not exist, a feature that was
compiled out. It is not a general-purpose error type for your program's
domain, and there is no variant to grow into for that. Define your own
#[derive(Error, Diagnostic)] enum and use Other only where the two worlds
meet. See Define your own error type.
Traits Error implements¶
Debug, Display, std::error::Error, miette::Diagnostic, and — because
every variant's payload is — Send + Sync + 'static. Those bounds are what
allow an Error to cross a thread boundary and to be converted into a
miette::Report, and there is a test that fails to compile if they are ever
lost.
Error is not Clone and not PartialEq. std::io::Error is neither, and
a boxed trait object cannot be either. Compare on the diagnostic code or the
Display string if you need to assert on one in a test.