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
Iterator
trait:When implementing
Iterator
whereItem
is 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
'a
is instantiated every call tostream_next
and 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 aVec
without 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
cursor
value. This also should be another hint for why this couldn't have worked with the non-streamingIterator
trait.Since
StreamingIterator
is 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.