reqwest's "use of moved value: resp" error when handling response body in Rust

I'm working on a Rust project that uses the reqwest crate to send HTTP requests to the MEXC API. The task is simple: place a buy order using the MEXC API and handle responses. However, I’ve been encountering a persistent error in my code regarding the use of moved value.

Here’s the problem I’m running into:

The error:

error[E0382]: use of moved value: `resp`
   --> src/main.rs:129:21
    |
     112 |     let resp = client.post(&url)
    |         ---- move occurs because `resp` has type `reqwest::Response`, which does not implement the `Copy` trait
...
125 |         let error_text = resp.text().await?;
    |                               ------ `resp` moved due to this method call
...
130 |         let error = resp.error_for_status().unwrap_err();
    |                     ^^^^ value used here after move

I have already tried several different solutions to resolve this:

Checked response status before consuming it: Used resp.status() and checked for success before consuming the body.

Cloning the body: I attempted cloning the response body before logging it, but the error still persisted.

Handling error separately: Ensured error responses are handled without moving the response prematurely. Despite these efforts, the error keeps popping up every time I try to consume the response body using .text() or .json().

My Current Code (simplified to relevant parts):

let resp = client.post(&url)
    .header("X-MEXC-APIKEY", API_KEY)
    .json(&body)
    .send()
    .await?;

if resp.status().is_success() {
    let result = resp.json::<serde_json::Value>().await?;
    println!("Order placed successfully: {:?}", result);
    return Ok(result);
} else {
    let error_text = resp.text().await?;
    eprintln!("Failed to place order. Error: {}", error_text);

    let error = resp.error_for_status().unwrap_err();
    eprintln!("Error response: {:?}", error);

    return Err(reqwest::Error::from(error));
}

I’ve also checked the Rust documentation for reqwest, and the error message says that reqwest::Response::text() takes ownership of the response, which is why it gets moved, but I’m unsure of how to avoid this.`

How can I safely check the status of the response and consume the body without moving the response?

Is there a better approach to consuming the body of reqwest::Response that avoids the "use of moved value" error?

Should I be using a different method from reqwest to handle this more efficiently?

Could this issue be related to the way reqwest handles certain HTTP statuses, or should I adjust my error handling differently?

Additional Info:
Rust version: 1.57.0
reqwest version: 0.11.27
OS: Ubuntu 20.04
Dependencies used:
reqwest = "0.11.27"
serde = "1.0"
tokio = "1.0"
futures-util = "0.3"
tokio-tungstenite = "0.15"

Here's one way.

    if let Err(error) = resp.error_for_status_ref() {
        let error_text = resp.text().await?;
        eprintln!("Failed to place order. Error: {}", error_text);
        eprintln!("Error response: {:?}", error);
        return Err(reqwest::Error::from(error));
    } else if ! resp.status().is_success() {
        // Not a 4xx or 5xx response, but not a 2xx response either
        // This would panic with the `.unwrap_err()` call in your OP
        panic!("Unexpected status: {:?}", resp.status());
    }

    let result = resp.json::<serde_json::Value>().await?;
    // ...
2 Likes

@quinedot beat me to it, but I'll keep the duplicate solution code.

Use error_for_status_ref() and move it before consuming the body:

        let error = resp.error_for_status_ref().unwrap_err();
        eprintln!("Error response: {:?}", error);

        let error_text = resp.text().await?;
        eprintln!("Failed to place order. Error: {}", error_text);

The explanation is that both text() and error_for_status() methods consume the Response. You can see this in the documentation, the receiver is self (opposed to &self or &mut self). Because the type does not implement Copy, this receiver is moved into the method call (and it doesn't come back out when the call returns).

You are disallowed from consuming the value more than once, but you are allowed to borrow it, discard the borrows, and then consume it.

FWIW, this kind of error handling doesn't scale. But it's good enough for prototyping.

5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.