1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
//! # SNAFU vs. Failure //! //! This comparison was made against the examples in [the guide for //! failure 0.1.5][failure-guide]. //! //! [failure-guide]: https://rust-lang-nursery.github.io/failure/guidance.html //! //! ## "Strings as errors" //! //! It's unclear what benefit Failure provides here. If you are using this //! functionality, we recommend using the standard library's `Box<dyn //! Error>`: //! //! ```rust //! fn example() -> Result<(), Box<dyn std::error::Error>> { //! Err(format!("Something went bad: {}", 1 + 1))?; //! Ok(()) //! } //! ``` //! //! If you wanted to do something similar with SNAFU, you can create a //! single-variant error enum with `String` data: //! //! ```rust //! use snafu::Snafu; //! use std::ops::Range; //! //! #[derive(Debug, Snafu)] //! enum Error { //! Any { detail: String }, //! } //! //! fn check_range(x: usize, range: Range<usize>) -> Result<usize, Error> { //! if x < range.start { //! return Any { detail: format!("{} is below {}", x, range.start) }.fail(); //! } //! if x >= range.end { //! return Any { detail: format!("{} is above {}", x, range.end) }.fail(); //! } //! Ok(x) //! } //! ``` //! //! This could be enhanced in a few ways: //! //! - create methods on your `Error` type //! - create a custom macro //! - add a [`Backtrace`][Backtrace] to the enum variant //! //! For example: //! //! ```rust //! use snafu::{Backtrace, Snafu}; //! use std::ops::Range; //! //! #[derive(Debug, Snafu)] //! enum Error { //! Any { detail: String, backtrace: Backtrace }, //! } //! //! macro_rules! format_err { //! ($($arg:tt)*) => { Any { detail: format!($($arg)*) }.fail() } //! } //! //! fn check_range(x: usize, range: Range<usize>) -> Result<usize, Error> { //! if x < range.start { //! return format_err!("{} is below {}", x, range.start); //! } //! if x >= range.end { //! return format_err!("{} is above {}", x, range.end); //! } //! Ok(x) //! } //! ``` //! //! Please see the next section for the recommended pattern for this error. //! //! [Backtrace]: crate::Backtrace //! //! ## "A Custom Fail type" and "Using the Error type" //! //! These two idioms from Failure are combined into one primary use case //! in SNAFU. Additionally, SNAFU avoids the downsides listed in the //! Failure guide. //! //! You can represent multiple types of errors, allocation is not //! required, and you can include any extra information relevant to the //! error: //! //! ```rust //! use snafu::{ensure, Snafu}; //! use std::ops::Range; //! //! #[derive(Debug, Snafu)] //! enum Error { //! #[snafu(display("{} is below {}", value, bound))] //! Below { value: usize, bound: usize }, //! //! #[snafu(display("{} is above {}", value, bound))] //! Above { value: usize, bound: usize }, //! } //! //! fn check_range(value: usize, range: Range<usize>) -> Result<usize, Error> { //! ensure!(value >= range.start, Below { value, bound: range.start }); //! ensure!(value < range.end, Above { value, bound: range.end }); //! Ok(value) //! } //! ``` //! //! You do not have to have a one-to-one relationship between an //! underlying error and an error variant: //! //! ```rust //! use snafu::{Snafu, ResultExt}; //! use std::num::ParseIntError; //! //! #[derive(Debug, Snafu)] //! enum Error { //! #[snafu(display(r#"Could not parse the area code from "{}": {}"#, value, source))] //! AreaCodeInvalid { //! value: String, //! source: ParseIntError, //! }, //! //! #[snafu(display(r#"Could not parse the phone exchange from "{}": {}"#, value, source))] //! PhoneExchangeInvalid { //! value: String, //! source: ParseIntError, //! }, //! } //! //! fn two_errors_from_same_underlying_error(area_code: &str, exchange: &str) -> Result<(i32, i32), Error> { //! let area_code: i32 = area_code //! .parse() //! .context(AreaCodeInvalid { value: area_code })?; //! let exchange: i32 = exchange //! .parse() //! .context(PhoneExchangeInvalid { value: exchange })?; //! Ok((area_code, exchange)) //! } //! ``` //! //! ## "An Error and ErrorKind pair" //! //! If you choose to make your error type [opaque][], you can implement //! methods on the opaque type, allowing you to selectively choose what //! your public API is. //! //! This includes the ability to return a different public enum that //! users can match on without knowing the details of your error //! implementation. //! //! ```rust //! use snafu::Snafu; //! //! #[derive(Debug, Snafu)] //! enum InnerError { //! MyError1 { username: String }, //! MyError2 { username: String }, //! MyError3 { address: String }, //! } //! //! #[derive(Debug, Snafu)] //! pub struct Error(InnerError); //! //! #[derive(Debug, Copy, Clone, PartialEq, Eq)] //! pub enum ErrorKind { //! Authorization, //! Network, //! } //! //! impl Error { //! pub fn kind(&self) -> ErrorKind { //! use InnerError::*; //! //! match self.0 { //! MyError1 { .. } | MyError2 { .. } => ErrorKind::Authorization, //! MyError3 { .. } => ErrorKind::Network, //! } //! } //! //! pub fn username(&self) -> Option<&str> { //! use InnerError::*; //! //! match &self.0 { //! MyError1 { username } | MyError2 { username } => Some(username), //! _ => None, //! } //! } //! } //! //! # fn main() {} //! ``` //! //! [opaque]: crate::guide::opaque