Итераторы ржавчины и с нетерпением ждут (peek/multipeek)

Я пытаюсь использовать шаблон с итераторами в Rust и падать где-то, очевидно, просто.

Я хотел бы выполнить итерацию через контейнер и найти элемент с предикатом [A] (простой), но затем с нетерпением жду другого предиката и получить это значение [B] и использовать [B] для мутации [A] в в некотором роде. В этом случае [A] является изменяемым и [B] может быть неизменным; это не имеет никакого значения для меня, только для проверки чека (правильно).

Это поможет понять это с помощью простого сценария, поэтому я добавил небольшой фрагмент, чтобы люди могли увидеть проблему/попытку. Я играл с itertools и ломал в for/while петли, хотя я хочу оставаться как можно более идиоматичным.

Глубокий пример сценария

Найдите четное число, найдите следующее число, которое делится на 3 и добавьте к исходному номеру.

#[allow(unused)]
fn is_div_3(num: &u8) -> bool {
    num % 3 == 0
}

fn main() {
    let mut data: Vec<u8> = (0..100).collect();

    let count = data.iter_mut()
        .map(|x| {
            if *x % 2 == 0 {
                // loop through numbers forward to next is_div_3,
                // then x = x + that number
            }
            true
        })
        .count();

    println!("data {:?}, count was {} ", data, count);
}

игровая площадка

Ответ 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, ...], что кажется правильным.

Ответ 2

К сожалению, я немного опаздываю, но здесь идет.

Это не совсем красиво, но это не так плохо, как другое предложение:

let mut data: Vec<u8> = (1..100).collect();

{
    let mut mut_items = data.iter_mut();
    while let Some(x) = mut_items.next() {
        if *x % 2 == 0 {
            let slice = mut_items.into_slice();
            *x += *slice.iter().find(|&x| x % 3 == 0).unwrap();
            mut_items = slice.iter_mut();
        }
    }
}

println!("{:?}", data);

дает

[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]

как и решение Матью М.

Ключ должен использовать mut_items.into_slice() для "повторного" итератора, эффективно создавая локальный (и, следовательно, безопасный) клон итератора.