Cannot return value referencing local variable in closure

Hello! I am writing my pet app and spent a lot of time trying to figure out why this error occurs. I am building a future stream of ip's, which should ping each ip in parrallel, so I guess i must use buffer_unordered on a stream of futures. But compiler complains about variable fetcher4 which is an Arc. I would appreciate any help for this problem. Code snippet:

                        some_future_stream_of_strings
                        .map(move |sub| {
                            let fetcher4 = fetcher3_2.clone();
                            match Fetcher::parse_link_to_ip(&sub) {
                                Some(ip) => fetcher4
                                    .ping(ip)
                                    .inspect_err(|err| {
                                        error!("Unreachable sub: {}, err:{}", sub.clone(), err)
                                    })
                                    .map(|ping| ping.ok().map(|p| (sub.clone(), ip, p)))
                                    .boxed(),
                                None => {
                                    warn!("Failed to parse sub: {}", sub);
                                    futures::future::ready(None).boxed()
                                }
                            }
                        })
                        .buffer_unordered(conf.batch_size)

error:

error[E0515]: cannot return value referencing local variable `fetcher4`
  --> src/main.rs:79:29
   |
79 | / ...                   match Fetcher::parse_link_to_ip(&sub) {
80 | | ...                       Some(ip) => fetcher4
   | |                                       -------- `fetcher4` is borrowed here
81 | | ...                           .ping(ip)
82 | | ...                           .inspect_err(|err| {
...  |
91 | | ...                   }
   | |_______________________^ returns a value referencing data owned by the current function

The error sounds like that fetcher4.ping(ip) borrows from fetcher4 but without the concrete function signatures that is hard to tell. But my best guess would be that the return value of ping borrows in some way from fetcher4. Then this gets passed to the map where it is turned from an Option into a Result (I guess) and then put in a tuple which then gets boxed.

So long story short if the return value of ping still borrows from fetcher4 it will be part of the return value of the closure and that is exactly the error, that you cannot return something from a function that borrows from something that got initialized within the function (like fetcher4).

For how to fix it I think we need more information. Is fetcher3_2 a reference or moved in owned? Why do you create let fetcher4 = fetcher3_2.clone(); in the first place and not just use fetcher3_2?

thanks for reply. Here is signature of fetcher:

    async fn ping(&self, ip: IpAddr) -> Result<u32, ping_mod::Error>

fetcher3_2 is an Arc<Fetcher> which is declared above this code

                let fetcher3 = fetcher2.clone();
                let fetcher3_2 = fetcher2.clone();
                let dao3 = dao2.clone();
                let sub_groups = conf.sub_groups.clone();
                async move {
                    let par_stream = stream::iter(sub_groups)
                        .then(|group| {
                         ...
                        })
                        .map(move |sub| {
                            let fetcher4 = fetcher3_2.clone();
                            match Fetcher::parse_link_to_ip(&sub) {
                                Some(ip) => fetcher4
                                    .ping(ip)
                                    .inspect_err(|err| {
                                        error!("Unreachable sub: {}, err:{}", sub.clone(), err)
                                    })
                                    .map(|ping| ping.ok().map(|p| (sub.clone(), ip, p)))
                                    .boxed(),
                                None => {
                                    warn!("Failed to parse sub: {}", sub);
                                    futures::future::ready(None).boxed()
                                }
                            }
                        })
                        .buffer_unordered(conf.batch_size)
                       


The futures returned from async fn always capture all the input lifetimes (and other generics). You may be able to avoid that with something like

fn ping(
    &self, ip: IpAddr
) 
-> impl use<> + Future<Output = Result<u32, ping_mod::Error>> {
    // Do whatever you need to using `&self` directly -- maybe clone some
    // `Arc`s or whatever -- then
    async move {
        // Do the rest in here
    }
}

(The default notional desugaring would have use<'_>.)

Or maybe making a async fn ping_arc(self: Arc<Self>, ..) is more straightforward in this case, hard to know.

yeah as said above an async fn generates a future that persist past the original invocation of the function and so need it's inputs to have lifetime equal or greater than the future generated so the only solution is to either estract the use of fetcher from the future generated or have the arc moved into the function