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.
Modules
use snafu::prelude::*
to your code to quickly get started with
SNAFU.Macros
Structs
main
and test functions.Traits
Error
trait object, suitable
for use in Error::source
.Error
trait to
versions of Rust lacking them.Attribute Macros
main
functions and tests.Derive Macros
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.