Lifetime problems with an web::block

Hi there,

I am currently working on a web project using actix-web, and I encountered a problem I have troubles solving. I implemented a function parse_multipart returning HashMap. I get a value from that hashmap, create a struct from it and move it to a web::block closure. And that's where things break: because it is a reference, value doesn't live long enough. I could try to clone the value, but then I get problems with ownerships or problems with lifetime because the block I create the copy in is to short-living. This is the code:

#[post("/pictures/{id}")]
pub async fn update(
    ident: Identity,
    pool: web::Data<DbPool>,
    id: web::Path<i32>,
    mut payload: Multipart,
) -> Result<HttpResponse, Error> {
    if ident.identity().is_none() {
        return Result::Err(error::ErrorForbidden("You have to be logged in to see this page"));
    }

    let params = parse_multipart(&mut payload).await?;
    let metadata = match get_file(&params) {
        Some((filename, file)) => {
            let content_type = match new_mime_guess::from_path(&filename).first_raw() {
                Some(s) => s,
                None => "image/jpeg",
            };
            let len = file.metadata()?.len();
            Some((filename.clone(), file, content_type.to_owned(), len as i32))
        }
        _ => None,
    };

    let form = form_from_params(&params, ident.identity().unwrap().parse::<i32>().unwrap(), &metadata);

    let pool_ = pool.clone();
    let picture = web::block(move || {
        let conn = pool_.get()?;
        actions::get_picture(id.into_inner(), &conn)
    })
    .await
    .map_err(|e| error::ErrorInternalServerError(format!("Database error: {}", e)))?;

    let mut data = form.clone();
    data.author_id = Some(ident.identity().unwrap().parse::<i32>().unwrap());

    let res = web::block(move || {
        let conn = pool.get()?;
        actions::update_picture(picture.id, &data, &metadata, &conn)
    })
    .await;
    let res = Err(error::ErrorBadRequest("picture field is not a file"));

    if let Ok(picture) = res {
        Ok(HttpResponse::Found()
            .header(header::LOCATION, picture_uri(&picture))
            .finish())
    } else {
        let error = match res {
            Err(cause) => Some(cause.to_string()),
            Ok(_) => None,
        };

        let s = Edit {
            title: Some(&format!("Edit picture #{}", picture.id)),
            page_type: None,
            page_image: None,
            body_id: None,
            logged_in: true,
            picture: &picture,
            form_data: &form,
            error: &error,
        }
        .render()
        .unwrap();

        Ok(HttpResponse::Ok().content_type("text/html; charset=utf-8").body(s))
    }
}

The error occurs for the params hashmap: the file I wrap into the tuple in the get_file match is a reference. When I create a clone with file.try_clone()? the value lives long enough, but I get ownership problems because form_from_params overtakes ownership. When I try to store a reference of the clone, of course, it is not living long enough because it gets destroyed after the match block.

What is a common strategy for dealing with these kind of problems?

Best regards,
CK

I solved this by not putting the filehandle into the Option tuple, creating a copy of it and not giving it to form_from_params (because it doesn't need it, it only needs the rest of the metadata). But I would be interested nonetheless what a solution would've been, for learning purposes. I'm still a beginner.

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.