A more general principle: don't build your application data structures out of references, mutable or not. As a beginner, you should think of references as being primarily for the purposes of passing values to and from functions, not for building data structures. When you are starting out, any time you find yourself writing struct MyStruct<'a>, you should conclude that you are making a mistake. There are exceptions, but this should be considered an advanced topic until you are more comfortable with the typical ways to use references.
Instead, data structures should always own their contents.
Don't implement Iterator on your data structures. The only kind of iterator you can sometimes build that way is a consuming iterator — one where the data structure is gone afterward. In order to build an iterator over references, the thing being referred to (the contents of MyStruct) needs to be separate from the iterator itself, so that mutating the iterator doesn't invalidate the references to the items.
Instead you should define separate iterator structs, and then use those to implement IntoIterator for your structure or a reference to it. This is how the mutable iterator would look:
struct MyMutIter<'a> { ... }
impl Iterator for MyMutIter {
type Item = &'a mut i32;
fn next(&mut self) ...
}
impl<'a> IntoIterator for &'a mut MyStruct {
type Item = &'a mut i32;
type IntoIter = MyMutIter<'a>;
fn into_iter(self) -> MyMutIter<'a> {
MyMutIter { ... }
}
}
// The other two common `IntoIterator` implementations are:
// Consuming
impl IntoIterator for &'a mut MyStruct {
type Item = i32;
...
}
// Immutable reference
impl<'a> IntoIterator for &'a MyStruct {
type Item = &'a i32;
...
}
Then what would be the cleanest approach ?
Since your iterator
- is based on a
Vec, and - only steps forward,
you can build on top of the existing mutable iterator for slices, std::slice::IterMut. Start with my_struct.data.iter_mut(), put that in a field of your iterator struct, and call its next() instead of maintaining an index with self.curr += 1;. This way, you can make use of the standard library's existing unsafe code[1] instead of having to write your own.
Technically, it's also possible to build a slice iterator out of purely safe code with careful use of pattern matching. But that's a trick that doesn't generalize to other data structures. ↩︎