Expand description
Macros for ergonomic error handling with thiserror.
§Example
#[derive(Serialize, Deserialize)]
struct Config {/* some fields */}
// bad: doesn't return the path to config (the user won't be able to fix it)
fn parse_config_v1(path: PathBuf) -> io::Result<Config> {
let contents = read_to_string(&path)?;
let config = from_str(&contents).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
Ok(config)
}
// good: returns the path to config & the underlying deserialization error (the user will be able fix it)
fn parse_config_v2(path: PathBuf) -> Result<Config, ParseConfigError> {
use ParseConfigError::*;
let contents = handle!(read_to_string(&path), ReadToStringFailed, path);
let config = handle!(from_str(&contents), DeserializeFailed, path, contents);
Ok(config)
}
#[derive(Error, Debug)]
enum ParseConfigError {
#[error("failed to read file to string: '{path}'")]
ReadToStringFailed { path: PathBuf, source: std::io::Error },
#[error("failed to parse the file contents into config: '{path}'")]
DeserializeFailed { path: PathBuf, contents: String, source: serde_json::Error }
}Advantages:
parse_config_v2allows you to determine exactly what error has occurredparse_config_v2provides you with all information needed to fix the underlying issueparse_config_v2allows you to retry the call by reusing thepath(avoiding unnecessary clones)
Disadvantages:
parse_config_v2is longer
That means parse_config_v2 is strictly better but requires writing more code. However, with LLMs, writing more code is not an issue. Therefore, it’s better to use a more verbose approach v2, which provides you with better errors.
This crates provides the handle family of macros to simplify the error handling code.
§Better debugging
To improve your debugging experience: call exit_result in main right before return, and it will display all information necessary to understand the root cause of the error:
pub fn main() -> ExitCode {
exit_result(run())
}This will produce a nice “error trace” like below:
- failed to run CLI command
- failed to run i18n update command
- failed to update 2 rows
- encountered 2 errors
* - failed to send an i18n request for row 'Foo'
- failed to construct a JSON schema
- input must be a JSON object
* - failed to send an i18n request for row 'Bar'
- failed to send a request
- server at 239.143.73.1 did not respondMacros§
- handle
handle!is a better alternative tomap_errbecause it doesn’t capture any variables from the environment if the result isOk, only when the result isErr. By contrast, a closure passed tomap_erralways captures the variables from environment, regardless of whether the result isOkorErrUsehandle!if you need to pass owned variables to an error variant (which is returned only in case when result isErr) In addition, this macro captures the original error in thesourcevariable, and sets it as thesourcekey of the error variant- handle_
bool - Returns an error when the condition is true.
- handle_
discard handle_discardshould only be used when you want to discard the source error. This is discouraged. Prefer other handle-family macros that preserve the source error.- handle_
into_ iter - Collects results from any
IntoIterator, wrapping all errors into one variant. - handle_
iter - Collects results from an iterator, returning a variant that wraps all errors.
- handle_
iter_ of_ refs - Collects results while keeping the corresponding input items, returning
(outputs, items)on success. - handle_
opt - See also:
handle_opt_take! - handle_
opt_ take - This macro is an opposite of
handle_opt!- it returns an error if the option contains aSomevariant. - map_err
map_errshould be used only when the error variant doesn’t capture any owned variables (which is very rare), or exactly at the end of the block (in the position of returned expression).
Structs§
- Debug
AsDisplay - A wrapper that renders
Debugusing the inner type’sDisplayimplementation. This wrapper is needed for types that have an easy-to-understandDisplayimpl but hard-to-understandDebugimpl. - Display
AsDebug - A wrapper that renders
Displayusing the inner type’sDebugimplementation. - ErrVec
- An owned collection of errors
- Error
Displayer - Item
Error - Associates an error with the item that caused it.
Enums§
- Write
ToNamed Temp File Error - Errors returned by
write_to_named_temp_file. - Writeln
Error ToWriter AndFile Error - Errors returned by
writeln_error_to_writer_and_file.
Functions§
- eprintln_
error - Writes an error trace to stderr and, if possible, includes a path to the full error report.
- exit_
iterator_ of_ results_ print_ first - Converts an
impl IntoIterator<Item = Result<(), E>>into anExitCode, printing a detailed error trace on the first failure. - exit_
result - Converts a
Resultinto anExitCode, printing a detailed error trace on failure. - get_
root_ source - Returns the deepest source error in the error chain (the root cause).
- write_
to_ named_ temp_ file - Writes the provided buffer to a named temporary file and persists it to disk.
- writeln_
error_ to_ formatter - Writes a human-readable error trace to the provided formatter.
- writeln_
error_ to_ writer_ and_ file - Writes a human-readable error trace to the provided writer and persists the full debug output to a temp file.
Type Aliases§
- Path
BufDisplay - A
PathBufthat returns aDebugrepresentation inDisplayimpl.