1

I am new to Rust and trying to learn. I am using Rocket to create an API endpoint that passes in some key/value pairs. I've defined my structs like this:

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
#[derive(Debug)]
struct PostDocument<'r> {
    fields: Vec<FieldValues<'r>>
}

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
#[derive(Debug)]
struct FieldValues<'r> {
    name: &'r str,
    value: &'r str,
}

and am using them in an API endpoint like this:

#[post("/<index_name>", format="json", data="<message>")]
async fn new_document(message: Json<PostDocument<'_>>, index_name: &str) -> Json<AddDocumentResponse> {
    // code
}

The code won't compile, though complaining about the lifetime de the serde deserializer apparently creates, and that de must outlive `r. Here is the full error message:

error: lifetime may not live long enough
  --> src/add_file.rs:18:5
   |
14 | #[derive(Deserialize)]
   |          ----------- lifetime `'de` defined here
...
17 | struct PostDocument<'r> {
   |                     -- lifetime `'r` defined here
18 |     fields: Vec<FieldValues<'r>>
   |     ^^^^^^ requires that `'de` must outlive `'r`
   |
   = help: consider adding the following bound: `'de: 'r`

A: I'm not sure where or how to specify the bound: 'de: 'r (I'm new to rust, and that syntax isn't familiar, and I haven't been able to find a reference to it in the docs)

B: I have other endpoints that I've written in other files that define very similar structs that seem to have no issues. The code itself simple loops over the Vec and adds each one to a tantivy doc. If I comment out all the code in the method body that uses that parameter at all, the error still persists.

C: if I add a lifetime parameter called 'de, the compiler complains that it cannot deserialize if there is a lifetime parameter called 'de.

CleverPatrick
  • 9,261
  • 5
  • 63
  • 86

2 Answers2

2

Use String instead of &'r str, unless there is a performance constraint, in which case you should have probably used actix, it is better to keep things simple.

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
#[derive(Debug)]
struct FieldValues {
    name: String,
    value: String,
}

In case you insist on using &str, it is better to get the bytes array from Rocket using request.body or as a stream, then when you own it, you can try to deserialize from it. You will still have the problem that as soon as the scope ends, the body bytes will be dropped, and if you use the structs elsewhere you will get the lifetime error. String is better in this case.

Oussama Gammoudi
  • 685
  • 7
  • 13
2

The lifetime 'de is introduced by the macro #[derive(Deserialize)].

Try this:

#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
#[derive(Debug)]
struct PostDocument<'r> {
    #[serde(borrow)]
    fields: Vec<FieldValues<'r>>
}

For more info check the serde docs here: https://serde.rs/lifetimes.html

You'll also see there that serde borrows automatically for &str and &[u8]