Hello, rustaceans,
i have been trying to implement my own iterator trait.
trait Iterator<ItemType> {
fn next(&mut self) -> Option<ItemType>;
fn foreach<FunctionType: FnMut(ItemType) -> ()>(&mut self, mut function: FunctionType) {
while let Some(item) = self.next() {
function(item)
}
}
}
Explanation:
I know, that the std
has one, but because of the association type, i would not be able to implement the iterator trait multiple times with different return types. For example, if i have iterator structs, that have a reference for a vector (copying or consuming the vector can be inefficient or not needed), i can implement the iterator trait twice for the IterMut: one returning immutable references and one mutable ones (and if ItemType: Clone, then also the item itself).
pub struct IterRef<'a, Type> {
reference: &'a Vec<Type>,
index: usize,
}
pub struct IterMut<'a, Type> {
reference: &'a mut Vec<Type>,
index: usize,
}
impl<'a, Type> Iterator<&'a Type> for IterRef<'a, Type> {
fn next(&mut self) -> Option<&'a Type> {
let reference = self.reference.get(self.index);
self.index += 1;
return reference;
}
}
impl<'a, Type> Iterator<&'a Type> for IterMut<'a, Type> {
fn next(&mut self) -> Option<&'a Type> {
let reference: Option<&Type> = self.reference.get(self.index);
self.index += 1;
return reference; //compiler-error: lifetime may not live long enough
}
}
impl<'a, Type> Iterator<&'a mut Type> for IterMut<'a, Type> {
fn next(&mut self) -> Option<&'a mut Type> {
let reference: Option<&mut Type> = self.reference.get_mut(self.index);
self.index += 1;
return reference; //compiler-error: lifetime may not live long enough
}
}
But this implementations have two errors: the returned reference from the mutable iterator may not outlive the iterator. (And i know about the integer overflow, its an example code.)
From my lifetime understanding, the immutable iterator is compiling, as the all references are immutable and therefor the vector and its items are never reallocated. The mutable iterator on the other side is capable of changing the vector and the items, so references returned from the iterator should be dropped befor the next iter.next()
is called. That is my understanding of the compiler error.
To fix the errors i have then added a lifetime for the &'a mut self reference in the next function:
trait Iterator<'a, ItemType> {
fn next(&'a mut self) -> Option<ItemType>;
...
}
impl<'a, Type> Iterator<'a, &'a Type> for IterMut<'a, Type> {
fn next(&'a mut self) -> Option<&'a Type> {...}
}
impl<'a, Type> Iterator<'a, &'a mut Type> for IterMut<'a, Type> {
fn next(&'a mut self) -> Option<&'a mut Type> {...}
}
The compiler complains now, that in the foreach
function the &mut self dont outlives the 'a lifetime requiered by the next
function. I dont understand this exactly, but it may be required by some outer code / lifetimes:
Error:
4 | while let Some(item) = self.next() {
| ^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
Fix:
fn foreach<FunctionType: FnMut(ItemType) -> ()>(&'a mut self, mut function: FunctionType) {
But one error is still present:
| -- lifetime `'a` defined here
...
4 | while let Some(item) = self.next() {
| ^^^^^^^^^^^
| |
| `*self` was mutably borrowed here in the previous iteration of the loop
| argument requires that `*self` is borrowed for `'a`
and i could'nt find any solution for this one. I know about loop unrolling and tried therefor to wrap the while content in {...}
brackets to drop the returned items. I also tried just to call self.next()
multiple times in a row (and wrapping in {...}
), nothing helped. I went so far introducing new lifetimes and subtyping them (and fn next<'b>(&'b mut self) -> Option<ItemType>
is the same as fn next(&mut self) -> Option<ItemType>
, if i introduce the subtyping i get the first or the third error depending on the bounds).
Summery:
I would like to have an iterator trait, which i can implement multiple times with different ItemTypes returned for the same struct (i am using them in my code alot and creating a struct for each ItemType necessary, and macros will make the code very complex as i would have to use them a lot, including inside generics). Implementing such iterator produces one of two errors:
- the returned reference dont outlive the iterator, or
- the
next()
function cannot be called more than once.
I would like to ask, if the goal i am aiming for is achivable in rust (may be with nightly?) by correcting my lifetimes / other code, if i should use other code structure or is this not achivable in rust at all?
Thanks in advantage for your time and your help.