Expand description
§SNAFU
SNAFU is a library to easily generate errors and add information to underlying errors, especially when the same underlying error type can occur in different contexts.
For detailed information, please see the Snafu
macro and the
user’s guide.
§Features
- Turnkey errors based on strings
- Custom error types
- Including a conversion path from turnkey errors
- Backtraces
- Extension traits for
- Suitable for libraries and applications
no_std
compatibility- Generic types and lifetimes
§Quick start
If you want to report errors without hassle, start with the
Whatever
type and the whatever!
macro:
use snafu::{prelude::*, Whatever};
fn is_valid_id(id: u16) -> Result<(), Whatever> {
if id < 10 {
whatever!("ID may not be less than 10, but it was {}", id);
}
Ok(())
}
You can also use it to wrap any other error:
use snafu::{prelude::*, Whatever};
fn read_config_file(path: &str) -> Result<String, Whatever> {
std::fs::read_to_string(path)
.with_whatever_context(|_| format!("Could not read file {}", path))
}
Whatever
allows for a short message and tracks a
Backtrace
for every error:
use snafu::{prelude::*, ErrorCompat, Whatever};
fn main() {
if let Err(e) = returns_an_error() {
eprintln!("An error occurred: {}", e);
if let Some(bt) = ErrorCompat::backtrace(&e) {
eprintln!("{}", bt);
}
}
}
§Custom error types
Many projects will hit limitations of the Whatever
type. When
that occurs, it’s time to create your own error type by deriving
Snafu
!
§Struct style
SNAFU will read your error struct definition and create a context
selector type (called InvalidIdSnafu
in this example). These
context selectors are used with the ensure!
macro to provide
ergonomic error creation:
use snafu::prelude::*;
#[derive(Debug, Snafu)]
#[snafu(display("ID may not be less than 10, but it was {id}"))]
struct InvalidIdError {
id: u16,
}
fn is_valid_id(id: u16) -> Result<(), InvalidIdError> {
ensure!(id >= 10, InvalidIdSnafu { id });
Ok(())
}
If you add a source
field to your error, you can then wrap an
underlying error using the context
extension method:
use snafu::prelude::*;
#[derive(Debug, Snafu)]
#[snafu(display("Could not read file {path}"))]
struct ConfigFileError {
source: std::io::Error,
path: String,
}
fn read_config_file(path: &str) -> Result<String, ConfigFileError> {
std::fs::read_to_string(path).context(ConfigFileSnafu { path })
}
§Enum style
While error structs are good for constrained cases, they don’t allow for reporting multiple possible kinds of errors at one time. Error enums solve that problem.
SNAFU will read your error enum definition and create a context
selector type for each variant (called InvalidIdSnafu
in this
example). These context selectors are used with the ensure!
macro to provide ergonomic error creation:
use snafu::prelude::*;
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("ID may not be less than 10, but it was {id}"))]
InvalidId { id: u16 },
}
fn is_valid_id(id: u16) -> Result<(), Error> {
ensure!(id >= 10, InvalidIdSnafu { id });
Ok(())
}
If you add a source
field to a variant, you can then wrap an
underlying error using the context
extension method:
use snafu::prelude::*;
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("Could not read file {path}"))]
ConfigFile {
source: std::io::Error,
path: String,
},
}
fn read_config_file(path: &str) -> Result<String, Error> {
std::fs::read_to_string(path).context(ConfigFileSnafu { path })
}
You can combine the power of the whatever!
macro with an
enum error type. This is great if you started out with
Whatever
and are moving to a custom error type:
use snafu::prelude::*;
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("ID may not be less than 10, but it was {id}"))]
InvalidId { id: u16 },
#[snafu(whatever, display("{message}"))]
Whatever {
message: String,
#[snafu(source(from(Box<dyn std::error::Error>, Some)))]
source: Option<Box<dyn std::error::Error>>,
},
}
fn is_valid_id(id: u16) -> Result<(), Error> {
ensure!(id >= 10, InvalidIdSnafu { id });
whatever!("Just kidding... this function always fails!");
Ok(())
}
You may wish to make the type Send
and/or Sync
, allowing
your error type to be used in multithreaded programs, by changing
dyn std::error::Error
to dyn std::error::Error + Send + Sync
.
§Next steps
Read the documentation for the Snafu
macro to see all of the
capabilities, then read the user’s guide for deeper
understanding.
Re-exports§
pub use report::__InternalExtractErrorType;
Modules§
- Not currently built; please add the
guide
feature flag. - Traits and macros used by most projects. Add
use snafu::prelude::*
to your code to quickly get started with SNAFU.
Macros§
- Ensure a condition is true. If it is not, return from the function with an error.
- Ensure a condition is true. If it is not, return a stringly-typed error message.
- Constructs a
Location
using the current file, line, and column. - Instantiate and return a stringly-typed error message.
Structs§
- A backtrace starting from the beginning of the thread.
- An iterator over an Error and its sources.
- An iterator over an Error and its sources that removes duplicated text from the error display strings.
- The source code location where the error was reported.
- Opinionated solution to format an error in a user-friendly way. Useful as the return type from
main
and test functions. - A basic error type that you can use as a first step to better error handling.
Traits§
- View a backtrace-like value as an optional backtrace.
- Converts the receiver into an
Error
trait object, suitable for use inError::source
. - Backports changes to the
Error
trait to versions of Rust lacking them. - Takes a string message and builds the corresponding error.
- Construct data to be included as part of an error. The data must require no arguments to be created.
- Combines an underlying error with additional information about the error.
- Additions to
Option
. - Additions to
Result
.
Attribute Macros§
- Adapts a function to provide user-friendly error output for
main
functions and tests.
Derive Macros§
- The
Snafu
macro is the entrypoint to defining your own error types. It is designed to require little configuration for the recommended and typical usecases while still offering flexibility for unique situations.