I am implementing a cursor over space grids, e.g. for a space [2, 2] the cursor should visit [0, 0], [1, 0], [0, 1], [1, 1] respectively. I have the code written below, but when I use for cursor in SpaceCursor::<2>::new(&[2, 2]), I noticed the cursor is not borrowed, but copied. Since the cursor will always within the SpaceCursor lifetime, I am wondering if I could have its type &[i32; 2] rather than [i32; 2]?
struct SpaceCursor<const D: usize> {
cursor: [i32; D],
boundary: [usize; D],
}
impl<const D: usize> SpaceCursor<D> {
pub fn new(boundary: &[usize]) -> Self {
let mut result = SpaceCursor::<D> {
cursor: [0; D],
boundary: boundary.try_into().expect("Bad boundary"),
};
if D >= 1 {
result.cursor[0] = -1;
}
result
}
}
impl<const D: usize> Iterator for SpaceCursor<D> {
type Item = [i32; D];
fn next(&mut self) -> Option<Self::Item> {
let mut index = 0;
while index < D {
self.cursor[index] += 1;
if self.cursor[index] < self.boundary[index] as i32 {
break;
}
index += 1;
}
if index == D {
None
} else {
for i in 0..index {
self.cursor[i] = 0;
}
Some(self.cursor)
}
}
}
If you need an iterator to yield references then the iterator itself must hold a reference to the underlying data. This is due to the design of the
Iteratortrait:When implementing
IteratorwhereItemis a reference, you would need to give it a lifetime. But you need to pick just one lifetime which then must be valid for all items, and that lifetime must outlive the iterator. This only works if all of the items are already held somewhere in memory and will live longer than the time that the iterator is being used.In Rust 1.65 this will (sort of) change. A new feature, generic associated types (GATs), will be available in stable, which will allow you to define a "streaming" iterator like this:
The difference here is that the lifetime
'ais instantiated every call tostream_nextand therefore can be different each time. The caller also gets to choose how long they want to keep the reference for and is only limited by the mutable borrow of the streaming iterator itself. The mutability also means you can only borrow one item at a time, so you couldn't do things like collect them into aVecwithout cloning them.Your iterator could be ported like this:
Note that the "limitation" of only being able to borrow one item at a time, is really critical to this working, because you're overwriting the
cursorvalue. This also should be another hint for why this couldn't have worked with the non-streamingIteratortrait.Since
StreamingIteratoris not a built-in trait, there is no syntactic support for it in loops, so you must use it explicitly:You can try this now in Rust 1.64 beta, in nightly, or wait about 2 weeks for Rust 1.65.