Borrowed value does not live long enough (lifetime issues)

fn get_data<'de, Lt: LastfmType<'de>>(url : Url) -> Result<Lt,lastfm_parse_rs::error::Error> {
  let mut data  = Vec::new();
  /* fill data */
  let result : Result<Lt, lastfm_parse_rs::error::Error> = from_json_slice(&data);
  result
}

fn main() {
    let r = get_data::<artist::GetInfo>("url");
}

the error:

30 |     let result : Result<Lt, lastfm_parse_rs::error::Error> = from_json_slice(&data);
   |                                                              ----------------^^^^^-
   |                                                              |               |
   |                                                              |               borrowed value does not live long enough
   |                                                              argument requires that `data` is borrowed for `'de`
31 |     result
32 | }
   | - `data` dropped here while still borrowed

How should I fix this?

The 'de lifetime is the lifetime of the data the value is parsed from, and it allows avoiding copying things from the original string when possible. In this case since you're throwing away the data vector before you finish using the item, you shouldn't be using 'de like that. Typically the solution is to use DeserializeOwned, but if you don't have access to how LastfmType interacts with deserialize, you can use LastfmType<'static> to only allow deserialization that copies the data into the resulting value.

So
fn get_data<Lt: LastfmType<'static>>(url : Url) -> Result<Lt,lastfm_parse_rs::error::Error>
?

Tried that, but the error remains the same

Ah, right, it's the other way around. I think you want Lt: for<'de> LastfmType<'de> instead.

fn get_data<Lt: for<'de> LastfmType<'de>>(url : Url) -> Result<Lt,lastfm_parse_rs::error::Error>
Like this?

Then I get the following error:

50 | match get_data::artist::GetInfo(url) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait for<'de> lastfm_parse_rs::LastfmType<'de> is not implemented for lastfm_parse_rs::artist::GetInfo<'_>

I think I will need to see the definition of GetInfo and LastfmType.

It's in this crate: https://github.com/xenzh/lastfm-parse-rs

Given that artist::GetInfo<'_> does not implement DeserializeOwned == for<'de> Deserialize<'de>, you will need to keep the bytes you deserialize from (i.e., your data vector) in scope: data must not be dropped before you are done using GetInfo (your program thus fails because data is dropped when the get_data function returns, which is right before the caller tries to get a GetInfo that refers / points to that (now stale) data.

There are multiple ways to design your code to keep data alive.

  1. Making data exist at the caller scope

    type Result<T> = ::std::result::Result<T, ::lastfm_parse_rs::error::Error>;
    
    fn get_data<'de, Lt: LastfmType<'de>>(
        url: Url,
        data: &'de mut Vec<u8>
    ) -> Result<Lt>
    {
        data.clear();
        /* fill data */
        from_json_slice(&data)
    }
    
    fn main ()
    {
        let mut data = Vec::new();
        let artist_info: artist::GetInfo = get_data("url", &mut data).unwrap();
    }
    

    This should work fine, but then there is the whole "the caller must provide this extra data parameter that they need to have previously set up, and which will be unusable for as long as we are using / moving the obtained artist_info is around".

    • The fact that data cannot be used can be alleviated using shadowing:

      let ref mut artist_info = Vec::new();
      let artist_info = get_data("url", artist_info).unwrap();
      

    But there is another pattern that makes this easier to use from a caller perspective: CPS, i.e., using a callback rather than a let binding to obtain the artist_info value.

  2. Using a callback

    This is how using a callback-based API looks like:

    fn main ()
    {
        with_artist_info("url", |r: Result<artist::GetInfo<'_>>| {
            let artist_info = r.unwrap();
            // use artist_info here
        });
    }
    

    And this is how you implement it:

    type Result<T> = ::std::result::Result<T, ::lastfm_parse_rs::error::Error>;
    
    fn get_data<'de, Lt: LastfmType<'de>>(
        url: Url,
        data: &'de mut Vec<u8>
    ) -> Result<Lt>
    {
        data.clear();
        /* fill data */
        from_json_slice(&data)
    }
    
    fn with_artist_info<R, F> (
        url: Url,
        f: F,
    ) -> R
    where
        F : FnOnce(Result<artist::GetInfo<'_>>) -> R,
    {
        let ref mut data = Vec::new();
        f(get_data(url, data))
    }
    
3 Likes

To get a generic callback interface, you need to have GATs, to express it nicely. Number 2 looks like chalk could solve it, and number 3 should work, once that gets fixed.

Thanks for all the explaining!
I went with the easy solution (1), but the error remains the same:

error[E0597]: `data` does not live long enough
  --> src/main.rs:35:21
   |
16 | fn get_data<'de, Lt: LastfmType<'de>>(
   |             --- lifetime `'de` defined here
...
35 |     from_json_slice(&data)
   |     ----------------^^^^^-
   |     |               |
   |     |               borrowed value does not live long enough
   |     argument requires that `data` is borrowed for `'de`
36 | }
   | - `data` dropped here while still borrowed

data should be &'de mut Vec<_>, not a Vec<_>