Rust reference dropped here while still borrowed
Asked Answered
U

1

12

The issue: I'm getting a "XXXXXXX dropped here while still borrowed" error from a method where XXXXXXX is valid to the end of the method (which is fine), but Rust is unnecessarily expecting it to live as long as the input references because those have an explicit lifetime.

Here's some setup information: I have a struct with iterates all found query within text content.

pub struct QueryRangeItr<'a> {
    inverted: bool,
    query: &'a str,
    content: &'a str,
}

It takes a reference of the query and content and expects the instance to last as long as the query/content. So far, so good.

impl<'a> QueryRangeItr<'a> {
    fn new(query: &'a str, content: &'a str, inverted: bool) -> QueryRangeItr<'a> {
        Self {
            inverted,
            query,
            content,
        }
    }
}

I have a static method that allows you to perform a transformation on each found query. It takes a closure and applies it to the queries, then gives you a new String which has no lifetime dependencies. Because the return value has no lifetime, I don't actually want to bring lifetimes to the query and content here, but if I don't, Rust tells me I need to... thus, they have lifetime requirements. By itself, this works fine.

The problem occurs on another static method that calls transform twice to transform both the query and non-query content. It first calls transfrom to apply one closure to found query. The results are fed back to another call of the transform method, but this time to apply a transform to the rest of the content.

impl<'a> QueryRangeItr<'a> {
    pub fn transform<T>(
        query: &'a str,
        content: &'a str,
        transform: T,
        inverted: bool,
    ) -> String where T: Fn(&str) -> String {
        let selects = Self::new(query, content, true);
        // ...
        // returns a `String` with no lifetime dependency on input params
    }

    pub fn transform_all<TQ, TNQ>(
        query: &'a str,
        content: &'a str,
        transform_query: TQ,
        transform_non_query: TNQ,
    ) -> String
        where
            TQ: Fn(&str) -> String,
            TNQ: Fn(&str) -> String,
    {
        let transformed_content = Self::transform(query, content, &transform_query, false);
        let transformed_query = transform_query(query);
        let transformed = Self::transform(&transformed_query, &transformed_content, transform_non_query, true); // <--- Rust expects `transformed_content` and `transformed_query` to match the lifetime of `query` and `content`
        transformed
    }

}

transformed_content and transformed_query both die at the end of transform_all... which makes sense, but Rust wants them to last as long as the input params query and content ('a)... and the won't.

I don't actually need them to live any longer. Once I get transformed back, I have no further need for them. However, having lifetimes on query and content make Rust think they need to last longer than they really need to and I get this error:

115 | impl<'a> QueryRangeItr<'a> {
    |      -- lifetime `'a` defined here
...
200 |         let transformed = Self::transform(&transformed_query, &transformed_content, transform_non_query, true);
    |                           ------------------------------------^^^^^^^^^^^^^^^^^^^^----------------------------
    |                           |                                   |
    |                           |                                   borrowed value does not live long enough
    |                           argument requires that `transformed_content` is borrowed for `'a`
201 |         transformed
202 |     }
    |     - `transformed_content` dropped here while still borrowed

and if I remove lifetimes I get this error:

error[E0621]: explicit lifetime required in the type of `query`
   --> src/range/query_range_iterator.rs:130:23
    |
125 |         query: &str,
    |                ---- help: add explicit lifetime `'a` to the type of `query`: `&'a str`
...
130 |         let selects = Self::new(query, content, invert);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required

And, if I try to define a different lifetime that 'a ('b or '_) for my transform methods, I get something like this:

error[E0521]: borrowed data escapes outside of associated function
   --> src/range/query_range_iterator.rs:130:23
    |
126 |         content: &'_ str,
    |         ------- `content` is a reference that is only valid in the associated function body
...
130 |         let selects = Self::new(query, content, invert);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `content` escapes the associated function body here

What I want to do is find a way to prove to Rust that I don't need transformed_content and transformed_query as long as it thinks.

Any thoughts?

Ulent answered 25/10, 2020 at 5:26 Comment(1)
I asked a concise Question for a similar common rust error error[E0716] error E0716: temporary value dropped while borrowed (rust). It links back to this Question.Bookrack
S
7

Replace

 = Self::transform(&transformed_query, &transformed_content, ...
// ^^^^

with

 = QueryRangeItr::transform(&transformed_query, &transformed_content, ...
// ^^^^^^^^^^^^^

By using Self, you're using the same lifetime 'a for everything. And as you've pointed out, transformed_content and transformed_query only live within the function, so their lifetime is definitely shorter and cannot match 'a. Replacing Self with QueryRangeItr allows the compiler to pick a new lifetime 'a for the call.

Snob answered 25/10, 2020 at 11:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.