Blake Pettersson
Blake Pettersson

Reputation: 9087

Correct lifetime for Into<Foo> with reference as a parameter

I'm not sure what the appropriate question to ask here, but I have an issue with getting the correct lifetime set in a generic method. The full code is here, but the basic issue is that I have a method looking something like below, but I get a lifetime error. Nothing from the &params reference will be embedded into the struct that's returned from .into(), so this should be safe. What do I need to get this to work?

pub fn index<'a, T, R, P>(repository: T, query: &'a str) -> Result<Vec<<R as ToJson>::Attrs>, QueryStringParseError>
    where
        T: JsonApiRepository<'a, T = R>,
        P: 'a,
        R: ToJson,
        R: QueryString<'a, Params = P>,
        <R as ToJson>::Attrs: From<(R, &'a P)>,
        QueryStringParseError: From<<T as JsonApiRepository<'a>>::Error>
{
    let params = <R as QueryString>::from_str(query)?;
    let list = repository.find_all(&params)?;

    // What's the correct lifetime incantation for this to work?
    // No references will be embedded in `R::Attrs`, so this
    // should be safe
    let data: Vec<<R as ToJson>::Attrs> = list.into_iter().map(|e| (e, &params).into()).collect();
    Ok(data)
}

(amended question with error below)

rustc 1.16.0 (30cf806ef 2017-03-10)
error[E0373]: closure may outlive the current function, but it borrows `params`, which is owned by the current function
  --> <anon>:16:64
   |
16 |     let data: Vec<<R as ToJson>::Attrs> = list.into_iter().map(|e| (e, &params).into()).collect();
   |                                                                ^^^      ------ `params` is borrowed here
   |                                                                |
   |                                                                may outlive borrowed value `params`
   |
help: to force the closure to take ownership of `params` (and any other referenced variables), use the `move` keyword, as shown:
   |     let data: Vec<<R as ToJson>::Attrs> = list.into_iter().map(move |e| (e, &params).into()).collect();

Upvotes: 0

Views: 55

Answers (1)

Shepmaster
Shepmaster

Reputation: 431599

The error you get (which I don't know why you didn't share):

error[E0373]: closure may outlive the current function, but it borrows `params`, which is owned by the current function
  --> src/main.rs:22:64
   |
22 |     let data: Vec<<R as ToJson>::Attrs> = list.into_iter().map(|e| (e, &params).into()).collect();
   |                                                                ^^^      ------ `params` is borrowed here
   |                                                                |
   |                                                                may outlive borrowed value `params`
   |
help: to force the closure to take ownership of `params` (and any other referenced variables), use the `move` keyword, as shown:
   |     let data: Vec<<R as ToJson>::Attrs> = list.into_iter().map(move |e| (e, &params).into()).collect();

You create params inside your function, but then all of the trait bounds require that the lifetime of params must match the lifetime of the string passed in from outside. It's not possible to satisfy that bound, and it looks like the reference to params has to live longer than the function, via the closure. Thus the so-so error message.

You should not tie these lifetimes together. Use higher-ranked trait bounds instead:

pub fn index<T, R, P, S, F>
    (repository: T,
     query: &str)
     -> Result<JsonApiArray<<R as ToJson>::Attrs>, QueryStringParseError>
    where T: JsonApiRepository<T = R>,
          P: Params,
          P: Default,
          P: TypedParams<SortField = S, FilterField = F>,
          P: for<'b> TryFrom<(&'b str, SortOrder, P), Err = QueryStringParseError>,
          P: for<'b> TryFrom<(&'b str, Vec<&'b str>, P), Err = QueryStringParseError>,
          R: ToJson,
          R: for<'b> QueryString<'b, Params = P, SortField = S, FilterField = F>,
          R: JsonApiResource<Params = P>,
          <R as ToJson>::Attrs: for<'b> From<(R, &'b P)>,
          QueryStringParseError: From<<T as JsonApiRepository>::Error>
{

Upvotes: 2

Related Questions