How to convert Box<dyn Error> or should I do this another way?

Hi, Im continue encapsulate HTTP client base on reqwest. Now I wanna return Result<Option<Record>, Box<dyn Error>> type for different API with a common error type Box<dyn Error>. Everything is ok until Im tring to return some API error message:

async fn find_record(
    client: &Client,
    base_url: &Url,
    username: &str,
    token: &str,
    domain: &str,
    record_host: Option<&str>,
) -> Result<Option<Record>, Box<dyn Error>> {
    let url = base_url.join(&format!("{}/records", domain))?;

    let response = client
        .get(url)
        .basic_auth(username, Some(token))
        .send()
        .await?;

    println!("GET {} {}", response.url(), response.status());

    if let Err(e) = response.error_for_status_ref() {
        let api_error = response.json::<ApiError>().await?;

        // Can I combine the two lines into one? I have tried turbofish but it is not working.
        let ret: Box<dyn Error> =
            format!("API response error: {}", error(api_error.message)).into();
        return Err(ret);
    }

  // more code ...
}

Q1: What's the right way to implement the code I commented?

THX for any help.


Here is the error message I meet when I trying to use turbofish:

error[E0107]: wrong number of type arguments: expected 0, found 1
  --> src/dns_provider/name_com.rs:88:83
   |
88 |         return format!("API response error: {}", error(api_error.message)).into::<Box<dyn Error>>()
   |                                                                                   ^^^^^^^^^^^^^^ unexpected type argument

error[E0277]: the trait bound `std::result::Result<std::option::Option<dns_provider::name_com::Record>, std::boxed::Box<dyn std::error::Error>>: std::convert::From<std::string::String>` is not satisfied
  --> src/dns_provider/name_com.rs:88:76
   |
88 |         return format!("API response error: {}", error(api_error.message)).into::<Box<dyn Error>>()
   |                                                                            ^^^^ the trait `std::convert::From<std::string::String>` is not implemented for `std::result::Result<std::option::Option<dns_provider::name_com::Record>, std::boxed::Box<dyn std::error::Error>>`
   |
   = note: required because of the requirements on the impl of `std::convert::Into<std::result::Result<std::option::Option<dns_provider::name_com::Record>, std::boxed::Box<dyn std::error::Error>>>` for `std::string::String`

After reading it again I find it seems just because into() do not accept type arguments. :sweat_smile:

This works for me:

async fn ret_str_err() -> Result<(), Box<dyn std::error::Error>> {
    Err(format!("").into())
}

What's the error message you get?

You don't tell us what is wrong with it, but my guess is that you're getting an error that talks about Send or Sync. Change the error type to Box<dyn Error + Send + Sync> if so.

1 Like

Instead of return Err(ret) you could do Err(format!("yadayada'))?. The ? operator has built-in conversion already.

Clippy may complain that it's a bad style to use stand-alone Err()? used instead of a return. You can try to refactor this code to use .map_err() and .and_then() and such, instead of if let Err, so that the whole thing remains a single expression with a single ? operator.

1 Like

Sorry for forgeting paste the error message, I have put it at the end of post.
And really thanks for your help!

Thanks for your answer, this is just what I want! The ? operator can always suprise me. :joy:

Well, I found that it seems hard to use .map_err() here, because I need let api_error = response.json::<ApiError>().await?; to get the error message from API response. If I write .map_error() like this:

response.error_for_status_ref().map_err(|err| {
        let api_error = response.json::<ApiError>().await?;
        format!("API response error: {}", error(api_error.message))
    })?;

then I will get another error:

error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
  --> src/dns_provider/name_com.rs:86:25
   |
85 |       response.error_for_status_ref().map_err(|err| {
   |  _____________________________________________-
86 | |         let api_error = response.json::<ApiError>().await?;
   | |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a closure that returns `std::string::String`
87 | |         format!("API response error: {}", error(api_error.message))
88 | |     })?;
   | |_____- this function should return `Result` or `Option` to accept `?`
   |
   = help: the trait `std::ops::Try` is not implemented for `std::string::String`
   = note: required by `std::ops::Try::from_error`