While I was developing a rust crate ([paypal-rs](https://github.com/edg-l/paypal-rs)) I noticed my error handling was pretty bad.
In that crate I had to handle 2 different types of errors:
- HTTP related errors, in this case `reqwest::Error`
- Paypal API errors, which I represent with my own struct `PaypalError`.
Initially I used [anyhow](https://github.com/dtolnay/anyhow) but then I found out this is pretty much only good to be used on binary applications, not in libraries.
The way to make this nice and clean for the library consumers is to wrap the errors.
## Wrapping the errors
First we need to know which errors need to be wrapped, in my case I have `PaypalError`:
```rust
/// A paypal api response error.
#[derive(Debug, Serialize, Deserialize)]
pub struct PaypalError {
// ...
}
// implement Error and Display for PaypalError...
```
And then an error from the reqwest library: `reqwest::Error`.
First we create an enum to represent all possible errors in our library:
```rust
#[derive(Debug)]
pub enum ResponseError {
/// paypal api error.
ApiError(PaypalError),
/// http error.
HttpError(reqwest::Error)
}
```
And as with any error, we have to implement `Error` and `fmt::Display`:
To solve this, we have to implement `From<PaypalError>` and `From<reqwest::Error>`:
```rust
impl From<PaypalError> for ResponseError {
fn from(e: PaypalError) -> Self {
ResponseError::ApiError(e)
}
}
impl From<reqwest::Error> for ResponseError {
fn from(e: reqwest::Error) -> Self {
ResponseError::HttpError(e)
}
}
```
And now our code becomes like this:
```rust
pub fn some_func() -> Result<(), ResponseError> {
func_call_returns_error()?;
Ok(())
}
```
## The library to skip all this process
There is a library called [thiserror](https://github.com/dtolnay/thiserror), which implements macros to make this process a breeze, here is how our code ends up if we use this library:
```rust
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ResponseError {
/// A paypal api error.
#[error("api error {0}")]
ApiError(#[from] PaypalError),
/// A http error.
#[error("http error {0}")]
HttpError(#[from] reqwest::Error)
}
```
And that's all the code we need!
This is equal (or maybe even better) than our previous code, the best is that it is entirely transparent, the library consumers won't even know `thiserror` was used, quoting their github:
> Thiserror deliberately does not appear in your public API. You get the same thing as if you had written an implementation of std::error::Error by hand, and switching from handwritten impls to thiserror or vice versa is not a breaking change.