Ellided lifetime not allowed here?

I am trying to get around an ownership issue in multer (not a github issue -- an issue I am having trying to use it). I need to parse each piece of the form and do something different with it, but because it takes ownership I can't. So I wrote a function to borrow it and return the data I need (string or bytes). However, I am getting an error despite attempting to explicitly state the lifetimes:

implicit elided lifetime not allowed here

I am also getting this error which I don't understand since I'm borrowing it and the error is inside a match statement:

cannot move out of `*f` which is behind a shared reference move occurs because `*f` has type `multer::Field<'_>`, which does not implement the `Copy`

async fn get_field<'a>(f: &'a Field, name:&'a str)->Result<fielddata,multer::Error>{
    match name{
        
            "content_type"=>{
                match f.content_type(){
                    Some(a)=>{return Ok(fielddata::s(a.to_string()))},
                    None=>{return Err(multer::Error::NoMultipart)}               }
            },
            "bytes"=>{
                match f.bytes().await{
                    Ok(a)=>{
                        return Ok(fielddata::b(a))
                    },
                    Err(e)=>{
                        return Err(e)
                    }
                }
            },
            "file_name"=>{
                match f.file_name(){
                    Some(a)=>{return Ok(fielddata::s(a.to_string()))},
                    None=>{return Err(multer::Error::NoMultipart)}
                }
            },
            "text"=>{
                match f.text().await{
                    Ok(a)=>{return Ok(fielddata::s(a))},
                    Err(e)=>{return Err(e)}
                }
            },
    }
}

How are you defining the fielddata type? If it has a lifetime parameter, then you'll need to set it to 'a in the return type of get_field().

does it need a lifetime?

enum fielddata{
    s(String),
    b(Bytes)
}

Alright, in that case it looks like it's not the fielddata but the Field that's the issue. Look at the full error message:

error[E0726]: implicit elided lifetime not allowed here
 --> src/main.rs:1:31
  |
1 | async fn get_field<'a>(f: &'a Field, name: &'a str) -> Result<fielddata, multer::Error> {
  |                               ^^^^^ expected lifetime parameter
  |
  = note: assuming a `'static` lifetime...
help: indicate the anonymous lifetime
  |
1 | async fn get_field<'a>(f: &'a Field<'_>, name: &'a str) -> Result<fielddata, multer::Error> {
  |                                    ++++

This is because multer::Field<'r> has a lifetime parameter.

Your next issue is that Field::bytes() and Field::text() consume the Field<'_>; notice how they take self, not &self. This is also mentioned in the error message:

error[E0507]: cannot move out of `*f` which is behind a shared reference
   --> src/main.rs:7:26
    |
7   |         "bytes" => match f.bytes().await {
    |                          ^^-------
    |                          | |
    |                          | `*f` moved due to this method call
    |                          move occurs because `*f` has type `Field<'_>`, which does not implement the `Copy` trait
    |
note: `Field::<'r>::bytes` takes ownership of the receiver `self`, which moves `*f`
   --> /home/lm978/.cargo/registry/src/github.com-1ecc6299db9ec823/multer-2.0.4/src/field.rs:126:24
    |
126 |     pub async fn bytes(self) -> crate::Result<Bytes> {
    |                        ^^^^

(And so on for text().) So fundamentally, you can't borrow the byte or text content just from a &Field<'_>. If you want to access any part of the field in arbitrary order, I'd suggest reading out all the parts of each Field<'_> into your own type, which owns the String or Bytes with the content. Then, you can borrow from it all you want.

Not sure how to do that since calling the necessary functions to get the data to populate a new type all take ownership.

Maybe I need to find another library or just don't use multipart forms?

I'm trying something new using multipart forms and it's been quite a headache. In the past I would in Javascript on the client side, parse all the form fields into an object and send that to the server. Then on the server parse it however I like. Using other people's code gets to be quite the hassle. But only using my own is slow and quite limiting because I'm still pretty inexperienced. It's hard to find the balance and libraries that are close enough to how I would do it that I can wrap my head around how to handle them.

You get all the data out of the Field<'_> and consume it to populate the newtype. Then, you can borrow the data in the newtype however you want. For instance:

use bytes::Bytes;
use mime::Mime;
use multer::Field;

struct FieldData {
    file_name: Option<String>,
    content_type: Option<Mime>,
    /* ... */
    bytes: Bytes,
}

async fn extract_data(field: Field<'_>) -> Result<FieldData, multer::Error> {
    let file_name = field.file_name().map(str::to_string);
    let content_type = field.content_type().cloned();
    let bytes = field.bytes().await?;
    Ok(FieldData {
        file_name,
        content_type,
        bytes,
    })
}

Then, you can take a &FieldData reference in get_field().

(The reason that multer is so tricky to use like this is because the data can be arbitrarily large, so it's built around a streaming API. But in our case, as long as the data don't get too large, there isn't much of a consequence to copying it.)

Yeah, and the reason I'm using multer is because it has the constraints option. Which I am using to prevent resource exhaustion. Thank you!

Still have the issue of:

use of moved value: `field`

bytes() and text() both take ownership and that seems to be the root of all my problems here.

struct FieldData {
    file_name: Option<String>,
    content_type: Option<Mime>,
    text: String,
    bytes: Bytes,
}

async fn extract_data(field: Field<'_>) -> Result<FieldData, multer::Error> {
    let file_name = field.file_name().map(str::to_string);
    let content_type = field.content_type().cloned();
    let text = field.text().await?;
    let bytes = field.bytes().await?;
    Ok(FieldData {
        file_name,
        content_type,
        text,
        bytes,
    })
}

There's nothing special about the text(): it's just the bytes() validated as UTF-8. We can use str::from_utf8() to generate the text from the bytes ourselves.

use bytes::Bytes;
use mime::Mime;
use multer::Field;
use std::str;

struct FieldData {
    file_name: Option<String>,
    content_type: Option<Mime>,
    bytes: Bytes,
    text: Option<String>,
}

async fn extract_data(field: Field<'_>) -> Result<FieldData, multer::Error> {
    let file_name = field.file_name().map(str::to_string);
    let content_type = field.content_type().cloned();
    let bytes = field.bytes().await?;
    let text = str::from_utf8(&bytes).ok().map(str::to_string);
    Ok(FieldData {
        file_name,
        content_type,
        bytes,
        text,
    })
}

(If you're sure that the data is valid UTF-8, then you can either use data.text.as_ref().unwrap() to borrow text, or use str::from_utf8(&bytes).unwrap().to_string() to create text.)

Thanks! I literally just figured this out digging into the library code this morning trying to understand why they're mutually exclusive.

It makes sense now that you would only want to use one or the other. Never both. It's either data (like an image or file) that should be saved as the output from bytes(), or it's text that should be saved as the output of text().

I didn't understand that bit without digging into the library. I guess I just assumed they were looking at different parts of the formdata, but they're not.

I'm 100% able to move on now and have learned a bit. Thank you SO much for your help.

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.