I like the ?
syntax a lot because it is very readable and delegates errors nicely but it often becomes unwieldly because ?
only coerces between like-typed error types.
If you define a type which contains each error type and the necessary impls for automatic type coercion, your life becomes much easier.
This macro does that. You define a list of all possible error types, and this macro spits out a system which you need only import.
pub mod error;
pub use crate::error::*; // Include this line in each file you intend to use `error` in.
// Notice how by importing the `error` module, you can omit the `Error` generic type on the result
pub fn a_function_that_can_fail_in_numerous_ways() -> Result<()> {
open_window()?;
read_file()?;
return Err(Error::from(ManualError::MiscellaneousError("Fail in some way that is known to you")))
}
You will need to define a name for your error collection. I like global
because I generally only have one per crate but you can pretty much define as many as you need.
In the parentheses you can place names of various other error marker types. These can't be parameterised because I couldn't be fucked to implement that. Instead use the ManualError
system. it's better anyway.
Each enumerable error type is given a name, followed by a type like so: Name = Error::Type;
.
For some other reason, I haven't managed to get trailing semicolons working, so you'll get compiler errors if you do that. So don't do that.
multi_error! { global()
ManualError = crate::error::ManualError; // because the `global` name defines a module, and Rust somehow isn't smart enough to resolve the path here, you end up with confusing path errors, saying that `ManualError` doesn't exist, while `super::ManualError` does. So I just specify it absolutely.
IoError = std::io::Error
}
pub enum ManualError {
ChildProcessDied { pid: usize },
CryptographicHashFunctionProducedACollision(String),
}
Enjoy <3