A few days ago, I ran into an issue where a trait object would be borrowed forever when a method was called on it. The solution there was to move a lifetime from the trait to the method itself.
Now I've run into a similar issue with the same method! Given the following method signature:
pub trait SearchByteStream<F: Read> {
fn in_byte_stream<'this, 'f>(&'this self, stream: F) -> Result<
Box<
// The iterator reads `F` and references data in `&self`, so it must live as long as both.
dyn Iterator<Item = Result<SearchResult, std::io::Error>> + 'f
>,
()
>
where
'this: 'f, // `self` should outlive `stream`.
F: 'f; // `stream`, however, doesn't need to live as long as `self`.
}
And the following call site:
let searchers: Vec<Box<dyn SearchByteStream<&File>>> = /* … */;
for path in &files_to_search {
let mut file = File::open(path)?;
for searcher in &searchers {
let results = searcher.in_byte_stream(&mut file);
}
}
I get the following error:
error[E0597]: `file` does not live long enough
--> source/main.rs:310:51
|
304 | let mut file = File::open(&path)?;
| -------- binding `file` declared here
...
310 | let results = searcher.in_byte_stream(&file);
| ^^^^^ borrowed value does not live long enough
...
345 | }
| - `file` dropped here while still borrowed
...
348 | }
| - borrow might be used here, when `searchers` is dropped and runs the `Drop` code for type `Vec`
|
= note: values in a scope are dropped in the opposite order they are defined
It appears that when searcher.in_byte_stream(&file)
is called, file
stays borrowed forever, even though the borrow should be released after the method call. What's even more mystifying is, if the declaration of searcher
is switched out for a concrete type…
let searchers: Vec<Box<RelativeSearcher>> = /* … */;
…the issue no longer occurs. In the previous thread, @nerditation kindly pointed me in the direction of the “borrow something forever” problem, which appears to be what's happening here, but if that is the case, I don't quite understand why…! And unlike last time, there's nowhere to move the lifetime declaration to.
Does anyone know ① why file
stays borrowed forever (since this issue keeps cropping up when I attempt dynamic dispatch, I really need to figure out what it stems from), and ② how to fix this issue?
The essential problem has to do with a Box<dyn Trait>
with a method that returns a Box<dyn Iterator<…>>
. I've prepared an interactive minimal example of the issue in Playground, in case that might help – it uses a trait named ReduceChunks
instead of SearchByteStream
and a method named chunk_results
instead of in_byte_stream
, but the principle is the same. Getting that minimal example to run should translate to the real-world case…!