0

I am trying to perform multiple concurrent GET requests using the reqwest library, while using a shared reference to the Client object. The idea is this will be a long-lived client, and as suggested in the docs, I would like to take advantage of the keep-alive connection pooling (otherwise I could just construct a new client for every loop iteration):

use std::error::Error;
use futures::future::join_all;
use tokio::task::JoinHandle;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let urls = vec![
        "https://www.rust-lang.org/tools/install",
        "https://www.rust-lang.org/learn",
        "https://play.rust-lang.org/"
    ];

    let client = reqwest::Client::builder().build()?;
    let mut tasks: Vec<JoinHandle<reqwest::Result<String>>>= vec![];

    for url in urls {
        tasks.push(tokio::spawn(async move {
            perform_fetch(&client, url).await
        }));
    }

    join_all(tasks).await;

    Ok(())
}

async fn perform_fetch(client: &reqwest::Client, url: &str) -> reqwest::Result<String> {
    client.get(url)
        .send()
        .await?
        .text()
        .await
}

However, I am getting a compiler error:

error[E0382]: use of moved value: `client`
  --> src\main.rs:17:44
   |
13 |       let client = reqwest::Client::builder().build()?;
   |           ------ move occurs because `client` has type `reqwest::Client`, which does not implement the `Copy` trait
...
17 |           tasks.push(tokio::spawn(async move {
   |  ____________________________________________^
18 | |             perform_fetch(&client, url).await
   | |                            ------ use occurs due to use in generator
19 | |         }));
   | |_________^ value moved here, in previous iteration of loop

I tried removing the move here:

tasks.push(tokio::spawn(async move {

But then I get another error:

error[E0373]: async block may outlive the current function, but it borrows `client`, which is owned by the current function
  --> src\main.rs:17:39
   |
17 |           tasks.push(tokio::spawn(async {
   |  _______________________________________^
18 | |             perform_fetch(&client, url).await
   | |                            ------ `client` is borrowed here
19 | |         }));
   | |_________^ may outlive borrowed value `client`
   |
   = note: async blocks are not executed immediately and must either take a reference or ownership of outside variables they use
help: to force the async block to take ownership of `client` (and any other referenced variables), use the `move` keyword
   |
17 |         tasks.push(tokio::spawn(async move {
   |                                       ++++

Is there a way the client reference can be shared between loop iterations without moving the value?

Here is the Cargo.toml for this example:

[package]
name = "concurrent-requests"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.21.2", features = ["full"] }
futures = "0.3"
reqwest = "0.11.12"

And the playground link:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=90758d69aa61523ff1ca4c18abd44b7f

Lukas S.
  • 5,698
  • 5
  • 35
  • 50
  • I don't think this should be closed because he's not sharing multiple requests via an Arc or Mutex! He wants to run multiple request futures in parallel, you can do this with FuturesUnordered like so: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7f2faf54cec2267b7c6b0839439dea0d – SeedyROM Dec 13 '22 at 04:50

0 Answers0