Ответ 1
Предупреждение. Итератор, представленный ниже, unsafe
, потому что он позволяет получать множественные псевдонимы одному элементу mutable; перейдите ко второй части для исправленной версии. (Было бы хорошо, если тип возвращаемого файла содержал неизменяемые ссылки).
Если вы готовы написать свой собственный итератор окна, то это станет довольно простым.
Во-первых, итератор во всех подробностях:
use std::marker::PhantomData;
struct WindowIterMut<'a, T>
where T: 'a
{
begin: *mut T,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<'a, T> WindowIterMut<'a, T>
where T: 'a
{
pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
WindowIterMut {
begin: slice.as_mut_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for WindowIterMut<'a, T>
where T: 'a
{
type Item = (&'a mut [T], &'a mut [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.len { return None; }
let slice: &'a mut [T] = unsafe {
std::slice::from_raw_parts_mut(self.begin, self.len)
};
let result = slice.split_at_mut(self.index);
self.index += 1;
Some(result)
}
}
Вызывается [1, 2, 3]
, он вернет (&[], &[1, 2, 3])
, затем (&[1], &[2, 3])
,... до (&[1, 2, 3], &[])
. Короче говоря, он итерации по всем потенциальным разделам среза (без перетасовки).
Что безопасно использовать как:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
for (head, tail) in WindowIterMut::new(&mut data) {
if let Some(element) = head.last_mut() {
if *element % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
*element += *n3;
}
}
}
}
println!("{:?}", data);
}
К сожалению, он также может использоваться как:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
let mut it = WindowIterMut::new(&mut data);
let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
let second_0 = &mut it.next().unwrap().0[0];
println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}
который при запуске print: 0x7f73a8435000 0x7f73a8435000
, show-casing, что обе изменяемые ссылки имеют один и тот же элемент.
Поскольку мы не можем избавиться от сглаживания, нам нужно избавиться от изменчивости; или, по крайней мере, отложить до внутренней изменчивости (Cell
здесь, так как u8
есть Copy
).
К счастью, Cell
не имеет затрат времени исполнения, но он немного стоит в эргономике (все эти .get()
и .set()
).
Я пользуюсь возможностью сделать итератор немного более общим и переименовать его, поскольку Window
уже используется для другой концепции.
struct FingerIter<'a, T>
where T: 'a
{
begin: *const T,
len: usize,
index: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> FingerIter<'a, T>
where T: 'a
{
pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
FingerIter {
begin: slice.as_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for FingerIter<'a, T>
where T: 'a
{
type Item = (&'a [T], &'a T, &'a [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len { return None; }
let slice: &'a [T] = unsafe {
std::slice::from_raw_parts(self.begin, self.len)
};
self.index += 1;
let result = slice.split_at(self.index);
Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
}
}
Мы используем его как строительный кирпич:
fn main() {
let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();
for (_, element, tail) in FingerIter::new(&data) {
if element.get() % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
element.set(element.get() + n3.get());
}
}
}
let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();
println!("{:?}", data);
}
В манере это печатает: [1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
, что кажется правильным.