Shmoopy
Shmoopy

Reputation: 5534

Handling non std::io::Error(s) while implementing std::io::Write?

I'm trying to implement std::io::Write over HTTP and I'm not sure how to handle errors that do not have a counterpart in std::io::ErrorKind.

Here's a short reproduction:

extern crate reqwest;

use std::io::Write;
use std::io::Result;

struct HttpClient {
    // Some configurations (compression, certificates, timeouts)
}

impl Write for HttpClient {
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        let client = ::reqwest::Client::builder().build()?;
        let res = client.post("http://httpbin.org/post").body(buf).send()?;
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<()> {
        Ok(())
    }
}

The compiler responds with 2 errors:

error[E0277]: the trait bound `std::io::Error: std::convert::From<reqwest::Error>` is not satisfied
  --> src/main.rs:12:22
   |
12 |         let client = ::reqwest::Client::builder().build()?;
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<reqwest::Error>` is not implemented for `std::io::Error`
   |
   = help: the following implementations were found:
             <std::io::Error as std::convert::From<std::io::ErrorKind>>
             <std::io::Error as std::convert::From<std::ffi::NulError>>
             <std::io::Error as std::convert::From<std::io::IntoInnerError<W>>>
             <std::io::Error as std::convert::From<serde_json::error::Error>>
             <std::io::Error as std::convert::From<openssl::error::ErrorStack>>
   = note: required by `std::convert::From::from`

error[E0277]: the trait bound `std::io::Error: std::convert::From<reqwest::Error>` is not satisfied
  --> src/main.rs:13:19
   |
13 |         let res = client.post("http://httpbin.org/post").body(buf).send()?;
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<reqwest::Error>` is not implemented for `std::io::Error`
   |
   = help: the following implementations were found:
             <std::io::Error as std::convert::From<std::io::ErrorKind>>
             <std::io::Error as std::convert::From<std::ffi::NulError>>
             <std::io::Error as std::convert::From<std::io::IntoInnerError<W>>>
             <std::io::Error as std::convert::From<serde_json::error::Error>>
             <std::io::Error as std::convert::From<openssl::error::ErrorStack>>
   = note: required by `std::convert::From::from`

There's a couple of things I could do, but I'm not happy with either of them:

  1. Use map_err to map reqwest::Error to std::io::Error - This is not always trivial. For example, how would I map TooManyRedirects? I could use std::io::ErrorKind::Other but it doesn't feel right.

  2. Define my own error type MyError and implement std::convert::From for reqwest::Error to MyError and for MyError to std::io::Error - This raises the same concerns from before - not all errors are easily convertible.

Are there any other better options here?

Upvotes: 0

Views: 615

Answers (1)

Shepmaster
Shepmaster

Reputation: 431139

Using io::Error is the only thing you can do because that's the contract that the trait requires. Everything else just boils down to details and ergonomics.

io::Error::new accepts an io::ErrorKind and something that can be converted into an error::Error.

I'd probably write a function that transforms your domain error into a io::Error by calling io::Error::new and then use this new function in map_err everywhere. I'd start by cramming everything into ErrorKind::Other until I found a reason that a specific Reqwest error should be something else.

Will your consumers really care about something specifically being too many redirects? By construction, the answer must be "no" because they might be operating on a File or a TcpSocket, neither of which have such a concept.

I don't believe I would create a wrapper error type in this case; I can't see how it would provide any value. It would require extra type annotations that you get "for free" with a function.

This is not always trivial.

That is correct — gluing two wildly different pieces together sometimes doesn't line up exactly the way we want. That's part of what makes programming both exciting and terrible.

Upvotes: 1

Related Questions