I'm new to Rust and like many finding lifetimes quite hard to get the hang of. I get the basics, but I can't quite fathom how to make this work.
I'm trying to implement an abstract data structure that holds a collection of records. One requirement is that the collection of records referenced by the data structure is represented as an Iterator
, because at times the records will be read from disk, at other times they'll come from memory, and at other times somewhere like a cache. The data structure itself doesn't care how the records are returned. It simply needs to be able to iterate over them and filter/map them etc.
My actual code is more involved than this, but I've distilled this down as much as possible to illustrate the issue I'm hitting.
Let's say I have a type Record
. It is not important what that is. If I have a Vec<Record>
called records
, then I can get an iterator over &Record
instances, using records.iter()
right? That's exactly what I need to store in a struct field, except that it needs to be abstract and not tied specifically to the implementation of Vec<T>
.
I have tried to define a trait as such:
trait RecordIterator: Iterator<Item = &Record> {}
And the struct that holds trait objects of this type:
struct RecordSet {
records_iter: Box<dyn RecordIterator>,
}
Of course this doesn't compile because I need to specify the lifetimes. The compiler suggests using higher rank lifetimes, so:
trait RecordIterator: for<'a> Iterator<Item = &'a Record> {}
impl<T> RecordIterator for T where T: for<'a> Iterator<Item = &'a Record> {}
But this gives me an error:
Compiling playground v0.0.1 (/playground)
error[E0582]: binding for associated type `Item` references lifetime `'a`, which does not appear in the trait input types
--> src/main.rs:3:40
|
3 | trait RecordIterator: for<'a> Iterator<Item = &'a Record> {}
| ^^^^^^^^^^^^^^^^^
error[E0582]: binding for associated type `Item` references lifetime `'a`, which does not appear in the trait input types
--> src/main.rs:5:56
|
5 | impl<T> RecordIterator for T where T: for<'a> Iterator<Item = &'a Record> {}
| ^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0582`.
error: could not compile `playground` due to 2 previous errors
Here is a link to the playground containing this code.
What do I need to do to store this kind of Iterator in the struct (as long as it is valid)? I'm also happy to avoid using Vec<T>::iter()
entirely and construct a new Iterator that basically does the same thing but meets the compiler's expectations if that is necessary.
A higher-ranked lifetime cannot appear only in associated types without appearing in generics too. I don't think this is impossible to implement, as far as I understand it was allowed once but was implemented incorrectly, causing unsoundness because the trait solver was sometimes choosing different lifetimes for the same instance. So it was just forbidden. I don't know if it's even hard to implement, or just nobody bothered.
But this doesn't matter because your definition is incorrect anyway. Take for example std::slice::Iter<'a, Record>
, created if you do vec.iter()
. This struct does not implement Iterator<Item = &'b Record>
for any lifetime 'b
, only for the lifetime 'a
- the lifetime of the vector. So what you need is a generic parameter, and not a higher-ranked trait bound:
pub trait RecordIterator<'a>: Iterator<Item = &'a Record> {}
impl<'a, T> RecordIterator<'a> for T where T: Iterator<Item = &'a Record> {}
pub struct RecordSet<'a> {
records_iter: Box<dyn RecordIterator<'a> + 'a>,
}