11

I am attempting to do a post using the reqwest library and following patters that I have found in various places online:

let res = http_client.post(&url)
                          .header("Content-Type", "application/x-www-form-urlencoded")
                          .form(&form_data)
                          .send()
                          .await?;
println!("authenticate response: {}", res.status)

The code block above causes a compile error:

`?` couldn't convert the error to `std::io::Error` the trait ` 
      `std::convert::From<reqwest::error::Error>` is not implemented for `std::io::Error`

I do not understand why I am getting this error. I have made my code as close to the examples as I can. It will compile if I remove the ? and the res.status. But I need to get the status res.status value. More importantly, I need to understand what I am missing or doing wrong.

E_net4
  • 27,810
  • 13
  • 101
  • 139
Jim Reineri
  • 2,830
  • 3
  • 31
  • 32
  • 1
    Does this answer your question? [What is the correct error part of the Result type of a function propagating different error types?](https://stackoverflow.com/questions/62100863/what-is-the-correct-error-part-of-the-result-type-of-a-function-propagating-diff) – E_net4 Jun 09 '20 at 14:45
  • @E_net4onMetatoomuchtime thanks for the link. It does add some background , but the answer below did directly solve my problem. Thanks for the additional info. – Jim Reineri Jun 10 '20 at 11:39

2 Answers2

12

Your error is caused by the return type of the function from where you call this block, the compiler wants to return a error of the type std::io::Error.

From the error description, I draw the conclusion your main function looks something like this:

fn main() -> Result<(), std::io::Error> {
    // ...
}

The problem is that reqwest does not return an io::Error. This is a snippet I wrote a few months ago:

use exitfailure::ExitFailure;

fn main() -> Result<(), ExitFailure> {
    let res = http_client
        .post(&url)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .form(&form_data)
        .send()
        .await?;

    println!("authenticate response: {}", res.status);
    Ok(())
}

I used exitfailure to convert the returned error type to a human-readable return type for main. I think it will solve your issue too.

Update: It was pointed out in the comments, exifailure have deprecated dependencies and this could be achieved without an external crate. There is a recommended way of returning an encapsulated dynamic error: Box<dyn std::error::Error>

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let res = http_client
        .post(&url)
        .header("Content-Type", "application/x-www-form-urlencoded")
        .form(&form_data)
        .send()
        .await?;

    println!("authenticate response: {}", res.status);
    Ok(())

}

From what I could see the behavior in returning agreageted errors from main is identical.

Simson
  • 3,373
  • 2
  • 24
  • 38
  • 2
    `Box` is [recommended by the book](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#the--operator-can-be-used-in-functions-that-return-result) and avoids extra dependencies. – Lambda Fairy Jun 09 '20 at 06:13
  • 3
    Especially since `exitfailure` depends on `failure`, which is [deprecated](https://github.com/rust-lang-nursery/failure#failure---a-new-error-management-story). – Jmb Jun 09 '20 at 06:30
  • TIL, thanks! The root cause of the problem is as I wrote. – Simson Jun 09 '20 at 06:36
  • I will check my code from this old project and correct my answer tonight when I have tested it. – Simson Jun 09 '20 at 06:38
0

map_err is used to convert the error types in Rust. its closure takes the error from the Result error and converts it to a new error. if you use in your project and hover over it, you see this

Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value, leaving an Ok value untouched.

This function can be used to pass through a successful result while handling an error.

use std::io::{Error,ErrorKind};


let res = http_client.post(&url)
                          .header("Content-Type", "application/x-www-form-urlencoded")
                          .form(&form_data)
                          .send()
                          .await; // removed ?
                          // ErrorKind is enum. you could choose different options
                          .map_err(|err|Error::new(ErrorKind::Other,"could not do post req"))?;
Yilmaz
  • 35,338
  • 10
  • 157
  • 202