2

How do I tell the compiler that one lifetime must outlive another?

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Tokens<'a> {
    buffer: String,
    list: Vec<Token<'a>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Token<'a> {
    term: &'a str,
}

yields

error: lifetime may not live long enough
 --> src/pipeline/tokenizers/test.rs:6:5
  |
3 | #[derive(Serialize, Deserialize, Debug)]
  |                     ----------- lifetime `'de` defined here
4 | pub struct Tokens<'a> {
  |                   -- lifetime `'a` defined here
5 |     buffer: String,
6 |     list: Vec<Token<'a>>,
  |     ^^^^ requires that `'de` must outlive `'a`
  |
  = help: consider adding the following bound: `'de: 'a`

In the code above, the token.term: &str will always refer to a slice of tokens.buffer. I'm not sure how to specify that relationship. I'm also not sure how to add the requested bound.

Will this even work? If so, what's the magic syntax?

ccleve
  • 15,239
  • 27
  • 91
  • 157
  • 1
    Two similar questions: https://stackoverflow.com/questions/57692768/how-to-derive-serdedeserialize-for-a-struct-with-members-with-lifetimes and https://stackoverflow.com/questions/56394620/why-can-serde-not-derive-deserialize-for-a-struct-containing-only-a-path. Though I'm not inclined to close this one as a dup. – Silvio Mayolo Oct 28 '22 at 19:18

2 Answers2

3

You can use the attribute, #[serde(borrow)], which makes the generated Deserialize implementation borrow the data.

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
pub struct Tokens<'a> {
    buffer: String,
    #[serde(borrow)]
    list: Vec<Token<'a>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Token<'a> {
    term: &'a str,
}

See:

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Thank you. This works. Is it necessary or possible to require that Token depend on Tokens.buffer? – ccleve Oct 28 '22 at 20:00
  • 1
    @ccleve One: no, serde will not make `list` refer to data in `buffer`, you would need a custom `Deserialize` implementation. Two: that would be creating a self-referential struct which has a whole host of problems, see [Why can't I store a value and a reference to that value in the same struct?](/q/32300132/2189130). – kmdreko Oct 28 '22 at 20:18
3

You can't add constraints to a derive implementation. You could write the entire Deserialize implementation yourself and put the constraint in the usual place. But fortunately, serde thought of this use case. If you indicate to serde that the data should be borrowed from the deserializer, it will put the constraint in for you.

#[derive(Serialize, Deserialize, Debug)]
pub struct Tokens<'a> {
    buffer: String,
    #[serde(borrow)]
    list: Vec<Token<'a>>,
}
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116