Tokio's spawn return type evaluates to the `'static` lifetime

Hello,
I have a problem with my function, which is supposed to spawn tokio tasks. Here's the code

async fn get_menu_for_dish_type(&self, _dish_type: DishType) -> Result<Menu, Error> {
        let _sub_pages_for_chosen_dish_type: Vec<PageConfig> = 
            self.sub_pages
                .iter()
                .cloned()
                .filter(|_sub_page_config| _sub_page_config.is_for_dish_type(_dish_type))
                .collect();
        
        if _sub_pages_for_chosen_dish_type.is_empty() {
            return Ok(Menu {_dish_type: _dish_type, _dishes: vec![] });
        }

        let _sub_pages_providers: Vec<KWDataProvider<&KWClient>> = _sub_pages_for_chosen_dish_type
            .into_iter()
            .map(|_sub_page_config| 
                PageDataProvider::new(
                    _sub_page_config,
                    &self.client,
                    0
                )
            )
            .collect();

        let mut _dishes: Vec<MenuItem> = vec![];
        
        let providers: Vec<tokio::task::JoinHandle<()>> = _sub_pages_providers.into_iter()
            .map(|_provider| {
                tokio::spawn(async move {
                    let _menu = _provider.get_page_menu_items().await.unwrap();
                })
            }).collect();

        for _each_provider in providers {
           _each_provider.await;
        }

        Ok(Menu {_dish_type: _dish_type, _dishes})
    }

and error says that:

error: cannot infer an appropriate lifetime
   --> src/data_sources/kwestiasmaku.rs:96:38
    |
96  |     async fn get_menu_for_dish_type(&self, _dish_type: DishType) -> Result<Menu, Error> {
    |                                      ^^^^ ...but this borrow...
...
127 |                 tokio::spawn(async move {
    |                 ------------ this return type evaluates to the `'static` lifetime...
    |
note: ...can't outlive the lifetime `'_` as defined on the function body at 29:1
   --> src/data_sources/kw.rs:29:1
    |
29  | #[async_trait]
    | ^
    = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

I have no idea why it occurs. The problem is related with self's lifetime, but I don't understand why tokio's spawn lifetime hase something to do with that.

I would really appriciate your help.

Thanks!

The closure passed to tokio::spawn can outlive the current function. This means that it is not allowed to borrow anything from the current function. It seems that PageDataProvider borrows from self.client. Try storing for exanple an Arc<Client> instead.

Every independently spawned task must have ownership of the values it uses. So if you try to use self but only got it by reference, it will fail as you don't have ownership to give away. And if you use a value in several tasks, it will fail, as ownership can only be given once.

You can use an Arc to give away shared ownership.

For more info, check out the Tokio tutorial. In particular the spawning chapter, although the shared state and channels chapters are also likely to be helpful.

Hmm, ok but what should be wrapped by Arc in this case? I have tried with self:

async fn get_menu_for_dish_type(&self, _dish_type: DishType) -> Result<Menu, Error> {
    let _sub_pages_for_chosen_dish_type: Vec<PageConfig> = 
        self.sub_pages
            .iter()
            .cloned()
            .filter(|_sub_page_config| _sub_page_config.is_for_dish_type(_dish_type))
            .collect();
    
    if _sub_pages_for_chosen_dish_type.is_empty() {
        return Ok(Menu {_dish_type: _dish_type, _dishes: vec![] });
    }

    let shared_self = Arc::new(self);
    let _sub_pages_providers: Vec<KWDataProvider<&KWClient>> = _sub_pages_for_chosen_dish_type
        .into_iter()
        .map(|_sub_page_config| {
            let self_copy = shared_self.clone();

            PageDataProvider::new(
                _sub_page_config,
                &self_copy.client,
                0
            )
        })
        .collect();

    let mut _dishes: Vec<MenuItem> = vec![];
    let providers: Vec<tokio::task::JoinHandle<()>> = _sub_pages_providers.into_iter()
        .map(|_provider| {
            tokio::spawn(async move {
                let _menu = _provider.get_page_menu_items().await.unwrap();
            })
        }).collect();

    for _each_provider in providers {
       _each_provider.await;
    }

    Ok(Menu {_dish_type: _dish_type, _dishes})
}

but unfortunatelly error remains the same. Currently tokio's spawn block uses only 'provider' which are an items of providers array which currently are initialized with arc, so It should work now, right?

Ok, problem solved by wrapping struct's 'client' into Arc<>. But there is one more problem, now with vector shared between tasks. Here is the problematic part:

let mut _dishes: Arc<Mutex<Vec<MenuItem>>> = Arc::new(Mutex::new(vec![]));

        let providers: Vec<tokio::task::JoinHandle<()>> = _sub_pages_providers.into_iter()
            .map(|_provider| {
                let _dishes_2 = _dishes.clone();
                tokio::spawn(async move {
                    let _menu = _provider.get_page_menu_items().await.unwrap();
                    _dishes_2.lock().unwrap().extend(_menu._dishes);
                })
            }).collect();

        for _each_provider in providers {
           let _ = _each_provider.await;
        }

        Ok(Menu {_dish_type: _dish_type, _dishes: *_dishes.lock().unwrap()})

and here is how the problem sounds like:

error[E0597]: `_dishes` does not live long enough
   --> src/data_sources/kwestiasmaku.rs:135:53
    |
135 |         Ok(Menu {_dish_type: _dish_type, _dishes: (*_dishes.lock().unwrap().to_owned()).to_vec()})
    |                                                     ^^^^^^^----------------
    |                                                     |
    |                                                     borrowed value does not live long enough
    |                                                     a temporary with access to the borrow is created here ...
136 |     }
    |     -
    |     |
    |     `_dishes` dropped here while still borrowed
    |     ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `std::sync::MutexGuard`
    |

I don't understand why I can't pass MutexGuard's return value like that?

Following code solved my problem:

    let _temp = &*_dishes.lock().unwrap();
    Ok(Menu {_dish_type: _dish_type, _dishes: _temp.to_vec()})

Anyone knows why do I have to provide that extra _temp variable and why do I have to use '&' there and then convert it to_vec() manually?

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.