Difficulty with lifetimes when updating a struct field with http request

Hi I am very new to Rust and I am trying to write my first program

The program gets some image metadata and then fetches the image with a http request using reqwest

This issue is I am unsure how to add the image_data to my Image struct after it has been instantiated

The RLS is showing

borrowed data escapes outside of async closure

reference escapes the async closure body hererustc(E0521)
image.rs(21, 9): `self` declared here, outside of the async closure body
image.rs(31, 17): reference escapes the async closure body here
image.rs(31, 40): borrow is only valid in the async closure body

While the compile error is

error[E0521]: borrowed data escapes outside of generator
  --> src/image.rs:31:17
   |
21 |         &mut self,
   |         --------- `self` is declared here, outside of the generator body
...
31 |                 self.image_data = Some(&res.bytes().await?);
   |                 ^^^^^^^^^^^^^^^^^^^^^^^-------------------^
   |                 |                      |
   |                 |                      borrow is only valid in the generator body
   |                 reference escapes the generator body here

error: aborting due to previous error

I cant seem to find a good example of what I am trying to do or how I should be going about setting this up so as not to run afoul of the borrow checker

Is using the Option the right way to go here or is there another more idiomatic approach?

Should I just be copying the data from the reqwest request into my struct?

The code is below and any help or a direction/search term/approach I should be looking for would be greatly appreciated!

#[derive(Debug)]
pub struct Image<'a> {
    pub id: i64,
    pub storage_id: i16,
    pub hash: &'a str,
    pub format: &'a str,
    pub width: i32,
    pub height: i32,
    pub size: i32,
    pub image_data: Option<&'a Bytes>, // pub image_data: Vec<u8>,
}

impl<'a> Image<'a> {

    async fn download_image(
        &mut self,
        httpClient: reqwest::Client,
    ) -> Result<(), Box<dyn std::error::Error>> {

        let url = format!(
            "{server}/{hash}",
            server = "localhost/images/",
            hash = &self.hash
        );

        let res = reqwest::get(&url).await?;

        self.image_data = Some(&res.bytes().await?);

        Ok(())
    }

}

Putting <'a> on a struct is a declaration that the fields marked with that lifetime are owned by some other variable somewhere else, and this struct is a borrow of that other variable. In particular this means that your struct must outlive the thing you're referencing.

This is not what's happening here: The image data is returned by bytes() and then a reference to that is taken. But it is essentially a local variable inside download_image, which means that the thing you are referencing does not outlive the Image (in fact it doesn't even outlive the function call).

To fix this, you need to take ownership instead. Use the type Option<Bytes> and don't take a reference when assigning it:

self.image_data = Some(res.bytes().await?);
2 Likes

Thanks Alice, that indeed did the trick and I appreciate the help.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.