use snafu::{Snafu, ResultExt};
#[derive(Debug, Snafu)]
enum Error {
#[snafu(display("Failed to read {}", filename))]
ReadInput { filename: String, source: std::io::Error },
}
type Result<T, E = Error> = std::result::Result<T, E>;
fn main() -> Result<()> {
let filename = "input.txt";
let s = std::fs::read_to_string(filename).context(ReadInput { filename })?;
println!("{}", s);
Ok(())
}
Unfortunately, this uses the default `Debug` formatting, so what is printed to the user in this `main` example is still less-than-ideal: Error: ReadInput { filename: "input.txt", source: Os { code: 2, kind: NotFound, message: "No such file or directory" } }
I encourage using the `Display` formatter or something more complete.I find that doing this rigorously creates what I call a "semantic backtrace", where the linked list of (error, context, underlying cause) provides a great deal of insight into the problem.
The next release of SNAFU will incorporate something that allows for stringly-typed errors:
use snafu::{ResultExt, Whatever};
type Result<T, E = Whatever> = std::result::Result<T, E>;
fn main() -> Result<()> {
let filename = "input.txt";
let s = std::fs::read_to_string(filename)
.with_whatever_context(|_| format!("couldn't read {}", filename))?;
println!("{}", s);
Ok(())
}
You will also be able to combine stringly-typed errors with the structured errors.Other features of the currently-released SNAFU:
- Custom error types
- Backtraces
- Extension traits for Results / Options / Futures / Streams
- Suitable for libraries and applications
- no-std compatibility
- Generic types and lifetimes
[SNAFU]: https://crates.io/crates/snafu