Cannot move out of `client`, a captured variable in an `FnMut` closure

Hello,
I'm stuck on a little problem in rust, the code is this one :

let client = reqwest::ClientBuilder::new().redirect(redirect::Policy::none()).build().unwrap();
 let mut bodies = stream::iter(urls)
     .map(|url| async move { (client.get(&url).send().await, url) })
     .buffer_unordered(workers);

And the error is this one :

error[E0507]: cannot move out of `client`, a captured variable in an `FnMut` closure
   --> src/main.rs:541:31
    |
539 |     let client = reqwest::ClientBuilder::new().redirect(redirect::Policy::none()).build().unwrap();
    |         ------ captured outer variable
540 |     let mut bodies = stream::iter(urls)
541 |         .map(|url| async move { (client.get(&url).send().await, url) })
    |                               ^^^------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |                               |  |
    |                               |  move occurs because `client` has type `reqwest::async_impl::client::Client`, which does not implement the `Copy` trait
    |                               |  move occurs due to use in generator
    |                               move out of `client` occurs here

I can't solve this issue, I have a lot of trouble with this closure borrowing thing...
Thank you

Remove the move on the async block to simply borrow it.

let mut bodies = stream::iter(vec!["test".to_string()])
    .map(|url| async { (client.get(&url).send().await, url) })
    .buffer_unordered(10);

Alternatively create the send future outside of the async block.

let mut bodies = stream::iter(vec!["test".to_string()])
    .map(|url| {
        let send_fut = client.get(&url).send();
        async move { (send_fut.await, url) }
    })
    .buffer_unordered(10);
1 Like

Oh it was that simple.. Thank you.
Is there one of the two ways that is better than the other please ? and why ?

They are pretty much equivalent in this case. The pattern of creating the future or some other resource in the closure is good to know, as it can be necessary to avoid lifetime issues in some cases, but it shouldn't matter here.

For example, if you need to do something that requires mutable access, then you can't do it in the async block, as buffer_unordered will run multiple async blocks at the same time, and mutable access requires exclusive access. However, the closure does not happen concurrently, and as such can do things that require mutable access.

My guess is that the latter version is slightly easier on the optimizer.

1 Like

Thank you,
But just to be sure, my function is really using the workers ?
I'm asking because of you sentence

However, the closure does not happen concurrently, and as such can do things that require mutable access.

is it only the closure that is not concurrent or the whole function is badly written and do not use the workers ?

Yes, it really is using the workers. It's just that only the code in the async block, and not the code in the closure, runs concurrently.

let mut bodies = stream::iter(vec!["test".to_string()])
    .map(|url| {
        // this part does not happen concurrently
        let send_fut = client.get(&url).send();
        async move {
            // but this part does
            (send_fut.await, url)
        }
    })
    .buffer_unordered(workers);
1 Like

Ok thank you !
I guess the question was a little stupid but I wanted to be sure I didn't make a mistake, rust is really fun but really hard for me too !

Thank you so much for your help !

1 Like

One interesting thing about async/await in Rust is that tasks are made asynchronous by repeatedly swapping the current task, but swapping can only happen at an .await. This is another way to see that the closure cannot be concurrent, as it cannot have an .await.

1 Like

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.