I have a memory region which I need to read some values from. These values are not necessarily contiguous in memory but are always some stride
bytes apart. I need to take these values and push them into some Vec<T>
.
I was doing it in a straightforward way like this and it worked fine:
// cursor is a *const u8
// values is a Vec<T>
for _ in 0..count {
let ptr = cursor as *const T;
values.push(*ptr);
cursor = cursor.add(stride);
}
As an exercise I wanted to add some syntactic sugar and implement an iterator so I could do something like this instead:
values.extend(strided_iter(start, count, stride));
So I went ahead and wrote this:
pub struct StridedRange<T: Sized> {
cursor: *const u8,
end: *const u8,
stride: usize,
_marker: PhantomData<T>,
}
impl<T> Iterator for StridedRange<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor < self.end {
let value = unsafe { *(self.cursor as *const Self::Item) };
self.cursor = unsafe { self.cursor.add(self.stride) };
Some(value)
} else {
None
}
}
}
pub unsafe fn strided_iter<T>(start: *const u8, count: usize, stride: usize) -> StridedRange<T> {
StridedRange {
cursor: start,
end: start.add(count * stride),
stride,
_marker: PhantomData,
}
}
(I know it won't work for .extend()
yet because it needs an IntoIterator
but that's a different problem)
Now if I try to use it for any T
that's not Copy
it won't compile with a message cannot move out of a raw pointer
. I don't understand why it happens because essentially I'm doing the same thing as my original code. Of course this is unsafe, but I thought that by using unsafe
I take responsibility for memory management and the compiler shouldn't care.
I would really appreciate if someone could explain why this is wrong and how one would go about implementing an iterator like this (and an IntoIterator
- in an idiomatic way).