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.
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
For detailed information, please see the user’s guide.
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
.
Modules
SNAFU user’s guide
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.
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.
The source code location where the error was reported.
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 in Error::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.
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.