1

I'm having trouble getting the reqwest crate to perform a bunch of async requests while reusing the same client. If I don't use a client and just use the provided get interface, everything works fine. Essentially I'm just fetching a list of items and then using async to fetch everything in the list.

However when I pass the client to my get_object closure the compiler complains the reference requires a static lifetime and while outlive 'client'.

How do I successfully annotate this lifetime? Or is there a better way to accomplish what I want?

extern crate futures; // 0.3.1
extern crate reqwest; // 0.10.1
extern crate serde; // 1.0.104
extern crate serde_json; // 1.0.45
extern crate tokio; // 0.2.10 

use serde::Deserialize;
use tokio::task;

#[derive(Debug, Deserialize)]
struct Item {
    name: Option<String>,
    id: Option<u64>,
}

pub trait ApiObject: Send {
    type Uri;

    fn from_json(text: &str) -> Result<Self, &'static str>
    where
        Self: Sized;

    fn url(uri: Self::Uri) -> String;
}

impl ApiObject for Item {
    type Uri = u64;

    fn from_json(text: &str) -> Result<Item, &'static str> {
        match serde_json::from_str::<Item>(text) {
            Result::Ok(val) => Ok(val),
            Result::Err(err) => match err.classify() {
                serde_json::error::Category::Data => Err("json input semantically incorrect"),
                serde_json::error::Category::Io => Err("failure to read/write bytes to io stream"),
                serde_json::error::Category::Syntax => Err("json input not syntactically valid"),
                serde_json::error::Category::Eof => Err("json data ended unexpectedly"),
            },
        }
    }

    fn url(uri: Self::Uri) -> String {
        format!("http://cats.com/items/{}.json", uri.to_string())
    }
}

/// Access point for any object inside the hybrid api
pub async fn get_object<O: 'static + ApiObject>(
    uri: O::Uri,
    client: &reqwest::Client,
) -> Result<O, &'static str> {
    let url = O::url(uri);

    let res = client.get(&url).send().await.unwrap();
    match res.status() {
        reqwest::StatusCode::OK => {
            let text = res.text().await.unwrap();
            task::spawn_blocking(move || to_struct(&text))
                .await
                .unwrap()
        }
        _ => Err("Request failed"),
    }
}

#[tokio::main]
async fn main() {
    let client = reqwest::Client::builder().build().unwrap();

    let top: Vec<u64> = vec![1, 2, 3, 4, 5];

    let futures: Vec<_> = top
        .iter()
        .map(|uri| get_object::<Item>(*uri, &client))
        .collect();

    let handles: Vec<_> = futures.into_iter().map(tokio::task::spawn).collect();

    let results = futures::future::join_all(handles).await;

    for result in results {
        let x = result.unwrap().unwrap();
        println!("{:#?}", x.name.unwrap());
    }
}

fn to_struct<T: ApiObject>(text: &str) -> Result<T, &'static str> {
    T::from_json(text)
}

playground

The error message:

error[E0597]: `client` does not live long enough
  --> src/main.rs:73:46
   |
73 |         .map(|uri| get_object::<Item>(*uri, &client))
   |              ----- --------------------------^^^^^^-
   |              |     |                         |
   |              |     |                         borrowed value does not live long enough
   |              |     returning this value requires that `client` is borrowed for `'static`
   |              value captured here
...
84 | }
   | - `client` dropped here while still borrowed

It differs from How can I perform parallel asynchronous HTTP GET requests with reqwest? because I'm using a closure to pass the client in — using an async move block did not seem to help me

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
jpsalm
  • 328
  • 1
  • 8
  • I have added a rust playground. It differs from the linked question because I'm using a closure to pass the client in -- using an async move block did not seem to help me. – jpsalm Feb 14 '20 at 20:07
  • [The duplicate applied to your question](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a03670299fc02bf87c89582134263f41). Ok to close now? – Shepmaster Feb 14 '20 at 20:19

0 Answers0