I created a straightforward data structure for dense 2D grids — see Grid2D module ($1777276) · Snippets · Snippets · GitLab — which works, but as I'm fairly new to the Rust language I have a few questions as to things that could be done better
The iterators GridIter and MutGridIter share all of the fields and most of their code, is there a way to write this more concisely (without macros)?
In MutGridiIter::next(&mut self) I needed to use an unsafe section to get around a lifetime issue (I think because there are two mutable references: one held by the iterator itself and one by the returnvalue). I'd strongly prefer not to do this, but I don't see another option.
// TODO cannot get rid of the unsafe here?
// error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in
// function call due to conflicting requirements
let ret = Some(((self.x, self.y), unsafe {
&mut *(&mut self.owner.grid[self.ofs] as *mut T)
}));
In Grid2D::get_mut I couldn't use the more elegant closure-based approach as in the get function. The compiler error mentions that the closure might outlive the current function but I don't think this is true? [solved]
Sadly, your code does not compile (playground) (ps. provide a link to the playground next time, we love playground links ), but that is very easy to fix
Sadly, your code does not compile (playground) (ps. provide a link to the playground next time, we love playground links ), but that is very easy to fix
Strange, it compiled locally. Maybe a Rust version difference?
Thanks for the suggestion, will add the lifetime and fix the closure !
You are running into this issue with the type system on the mutable iterator. The article has some outdated Rust syntax, but the concept still holds. In short, unsafe is the only way to yield mutable borrows from an iterator.
Thank you! Yes, that seems to be exactly the issue. I've thought about this a bit, as this is a fairly old issue it's unlikely to change any time soon, and I might abandon mutable iterators completely in my design.
Another option, as the base data structure is a Vec, might be to return a flattened iterator of vec.iter_mut for every 1D span (using their unsafe code!), though this sounds like a borrow-checking nightmare too.
Edit: the latter is also out of the question, I think
The problem in the article only occurs if you're returning overlapping slices, though, right? I skimmed the article but it seems to be mostly about "streaming iterators" which return data that originates within the iterator itself, or overlaps from previous slices. If I understand your example correctly, you are returning non-overlapping data from a vec not owned by the iterator (if you weren't it would be unsound to work around the bug and implement iterator anyway)
For cases where you are returning unique indices from a borrowed source, you should be able to implement a mutable iterator using only split_at_mut
This is definitely supposed to concern non-overlapping slices—for each cell in a region of a 2D grid it should yield ((x,y), &mut T). if it can be made to return overlapping rows that'd be a bug, the rectangle is clipped to avoid this.
My example in the previous was off the mark in that regard, I was quickly trying whether the principle would work, was planning to offset/restrict the iterators later!
Thank you! I had no idea that it was possible to use split_at_mut multiple times recursively, but that makes a lot of sense, I'll try this.
I don’t see any mention of overlapping in the article at all. In fact, it clearly points out that the iterator interface can only return unique references. The mere existence of the &mut data field on VecMutIterator is what causes the borrow checker to raise an error. In this case, it’s the &mut owner field on MutGridIter. Returning a mutable borrow from data that is already mutable borrowed by this struct is mutable aliasing.
The nomicon link does describe a way to resolve the aliasing though, and that’s by updating the mutable slice held in the owned field, and returning the other half of the split. I wasn’t aware of this technique, seems like it should work!
I've changed the next function to split up the slice, return a mutable reference to the current cell, and store (part of) the rest
type Item = ((i32, i32), &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
if self.y < self.y2 {
let (cell, rest) = self.slice.split_at_mut(1);
let ret = Some(((self.x, self.y), &mut cell[0]));
self.slice = rest;
// Advance
self.x += 1;
if self.x >= self.x2 {
self.x = self.x1;
self.y += 1;
if self.y < self.y2 { // don't advance out of slice
self.slice = self.slice.split_at_mut(self.stride).1;
}
}
ret
} else {
// out of range
None
}
}
I've tested that this approach works for the non-mutable iterator, however for the mutable one there are lifetime errors error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements;
It might be a matter of being more explicit about lifetimes, but this is way above my head.