Issue with boxed dyn trait, async and lifetimes

Hello, I'm having an issue with boxed dyn traits and lifetimes...

Here is the code:

use async_trait::async_trait;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
struct Item {}

#[async_trait]
trait FetchItems: Send + Sync {
    async fn fetch_items(&self) -> Vec<Item>;
}

struct ItemFetcher {
    item_fetchers: Vec<Box<dyn FetchItems>>,
}

impl ItemFetcher {
    async fn run(&self) -> Vec<Item> {
        let items: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(Vec::new()));
        let mut handles = vec![];

        for item_fetcher in self.item_fetchers.iter() {
            let items = items.clone();

            handles.push(tokio::spawn(async move {
                let mut fetched_items = item_fetcher.fetch_items().await;
                items.lock().unwrap().append(&mut fetched_items);
            }));
        }

        for handle in handles {
            handle.await.unwrap();
        }

        let items = items.lock().unwrap().to_vec();

        items
    }
}

struct SomeItemFetcher {}

#[async_trait]
impl FetchItems for SomeItemFetcher {
    async fn fetch_items(&self) -> Vec<Item> {
        vec![]
    }
}

#[tokio::main]
async fn main() {
    let item_fetcher = ItemFetcher {
        item_fetchers: vec![Box::new(SomeItemFetcher {})],
    };
    let items = item_fetcher.run().await;
}

And here is the error:

   Compiling playground v0.0.1 (/playground)
error[E0521]: borrowed data escapes outside of associated function
  --> src/main.rs:21:29
   |
17 |     async fn run(&self) -> Vec<Item> {
   |                  -----
   |                  |
   |                  `self` is a reference that is only valid in the associated function body
   |                  let's call the lifetime of this reference `'1`
...
21 |         for item_fetcher in self.item_fetchers.iter() {
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^
   |                             |
   |                             `self` escapes the associated function body here
   |                             argument requires that `'1` must outlive `'static`

For more information about this error, try `rustc --explain E0521`.
error: could not compile `playground` due to previous error

(Playground)

I've been searching for a while but I'm unable to find the solution, I've had other issues previously but they where fixed by adding the Send + Sync to the trait. This is the last one to get my code to compile, I'm sure it's something dumb...

Anyways if anybody could help me figure this out it would be awesome!

When you spawn the new task, you're passing item_fetcher, which is a ref to self.item_fetchers. That's why it can't work - a task's lifetime is potentially unlimited, so the task could potentially outlive item_fetcher.

If you change the Box to an Arc, it works:

(Rust Playground)[Playground]

2 Likes

Yes! That was it, thanks! :slight_smile:

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.