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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With