I thought I had learned enough about Rust now that I could try using the reqwest crate to send an HTTP request and get the response. I was clearly wrong. After about a half hour of trying every example I can find on the web, I'm ready to give up. Is this close?
extern crate reqwest;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut res = reqwest::get("https://httpbin.org/headers")?;
// copy the response body directly to stdout
std::io::copy(&mut res, &mut std::io::stdout())?;
Ok(())
}
Compiling playground v0.0.1 (/playground)
error[E0277]: the `?` operator can only be applied to values that implement `Try`
--> src/main.rs:4:19
|
4 | let mut res = reqwest::get("https://httpbin.org/headers")?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl Future`
|
= help: the trait `Try` is not implemented for `impl Future`
= note: required by `into_result`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`
To learn more, run the command again with --verbose.
Hi! I'm on mobile so I can't format code nicely in a reasonable amount of time, but I've fixed your example and added comments explaining why it didn't work: Playground.
Note that it still doesn't compile because the playground isn't set up to do networking (logically ).
The easiest fix, if you don’t want to go into async code yet, is to use reqwest::blocking::get instead of reqwest::get. With that change your code should compile.
For future reference by others, here's a version that parses the response body as JSON and deserializes it to a Rust struct (the commented out lines) or a vector of them (the uncommented lines).
extern crate reqwest;
extern crate tokio;
use serde::{Deserialize, Serialize};
use serde_json;
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Todo {
user_id: i32,
id: i32,
title: String,
completed: bool,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
//let url = "https://jsonplaceholder.typicode.com/todos/3";
let url = "https://jsonplaceholder.typicode.com/todos";
let res = reqwest::get(url).await?;
let json = res.text().await.unwrap();
println!("json = {}", json);
//let todo: Todo = serde_json::from_str(&json).unwrap();
let todos: Vec<Todo> = serde_json::from_str(&json).unwrap();
//println!("todo = {:?}", todo);
println!("todos = {:#?}", todos);
Ok(())
}
Because you can't directly return the unsized type dyn std::error::Error, you have to box it. Rust needs to know the size of types it returns from functions at compile time, and dyn std::error::Error could represent any type that implements the associated trait, so you need to box it to give it a fixed size (by putting the contents on the heap, and returning a pointer, i.e. boxing it)
std::error::Error is a trait, not a type. Many types are errors, but there is no single Error type. dyn Error is a feature of Rust called trait objects that allows you to store any value that implements a trait, so in this case we return any type that implements the Error trait. It has to be a Box because of the reasons Yato explained.
To add on to what @Kestrer said a little, some syntactical history can add to the confusion: A dyn Trait can also be written out as just Trait when it appears in a location that expects a type, such as a return type specifier. Not using dyn for a trait object is deprecated and produces a warning in the 2018 edition.