Использование итератора в качестве аргумента функции несколько раз из одного вектора - PullRequest
2 голосов
/ 05 июня 2019

Я пытаюсь написать код Rust для декодирования данных GPS с SDR-приемника. Я читаю образцы из файла и преобразовываю двоичные данные в серию комплексных чисел, что занимает много времени. Однако бывают случаи, когда я хочу передавать сэмплы, не сохраняя их в памяти (например, один очень большой файл, обработанный только одним способом, или сэмплы непосредственно из приемника), и другие случаи, когда я хочу сохранить весь набор данных в памяти (например, один маленький файл обрабатывается несколькими разными способами), чтобы избежать повторения операции разбора двоичного файла.

Поэтому я хочу написать функции или структуры с итераторами как можно более общими, но я знаю, что они не имеют размера, поэтому мне нужно поместить их в Box. Я ожидал, что что-то подобное сработает.

Это самый простой пример, который я мог бы привести, чтобы продемонстрировать ту же самую основную проблему.

fn sum_squares_plus(iter: Box<Iterator<Item = usize>>, x: usize) -> usize {
    let mut ans: usize = 0;
    for i in iter {
        ans += i * i;
    }
    ans + x
}

fn main() {
    // Pretend this is an expensive operation that I don't want to repeat five times
    let small_data: Vec<usize> = (0..10).collect();

    for x in 0..5 {
        // Want to iterate over immutable references to the elements of small_data
        let iterbox: Box<Iterator<Item = usize>> = Box::new(small_data.iter());
        println!("{}: {}", x, sum_squares_plus(iterbox, x));
    }

    // 0..100 is more than 0..10 and I'm only using it once,
    // so I want to 'stream' it instead of storing it all in memory
    let x = 55;
    println!("{}: {}", x, sum_squares_plus(Box::new(0..100), x));
}

Я пробовал несколько разных вариантов, но ни один из них не работает. В данном конкретном случае я получаю

error[E0271]: type mismatch resolving `<std::slice::Iter<'_, usize> as std::iter::Iterator>::Item == usize`
  --> src/main.rs:15:52
   |
15 |         let iterbox: Box<Iterator<Item = usize>> = Box::new(small_data.iter());
   |                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found usize
   |
   = note: expected type `&usize`
              found type `usize`
   = note: required for the cast to the object type `dyn std::iter::Iterator<Item = usize>`

Меня не беспокоит параллелизм, и я был бы рад просто заставить его работать последовательно в одном потоке, но параллельное решение было бы хорошим бонусом.

1 Ответ

2 голосов
/ 05 июня 2019

Текущая ошибка, с которой вы столкнулись, находится здесь:

let iterbox:Box<Iterator<Item = usize>> = Box::new(small_data.iter());

Вы заявляете, что вам нужен итератор, который возвращает usize элементов, но small_data.iter() - это итератор, который возвращает ссылки наusize предметов (&usize).Вот почему вы получаете ошибку «ожидаемая ссылка найдена».usize - это небольшой тип, который можно клонировать, поэтому вы можете просто использовать адаптер итератора .cloned() для создания итератора, который фактически возвращает размер.

let iterbox: Box<Iterator<Item = usize>> = Box::new(small_data.iter().cloned());

Как только вы преодолеете это препятствие, возникает следующая проблемаявляется то, что итератор, возвращенный над small_data, содержит ссылку на small_data.Поскольку sum_squares_plus определено для принятия Box<Iterator<Item = usize>>, в этой сигнатуре подразумевается, что объект черты Iterator в блоке имеет время жизни 'static.Итератор, который вы предоставляете, не делает этого, потому что он занимает small_data.Чтобы исправить это, вам нужно настроить определение sum_squares_plus на

fn sum_squares_plus<'a>(iter: Box<Iterator<Item = usize> + 'a>, x: usize) -> usize

Обратите внимание на аннотации 'a времени жизни.Затем код должен компилироваться, но если нет каких-либо ограничений, кроме того, что четко определено здесь, более идиоматичным и эффективным подходом было бы избегать использования объектов-признаков и связанных распределений.Приведенный ниже код должен работать с использованием статической диспетчеризации без каких-либо объектов-черт.

fn sum_squares_plus<I: Iterator<Item = usize>>(iter: I, x: usize) -> usize {
    let mut ans: usize = 0;
    for i in iter {
        ans += i * i;
    }
    ans + x
}

fn main() {
    // Pretend this is an expensive operation that I don't want to repeat five times
    let small_data: Vec<usize> = (0..10).collect();

    for x in 0..5 {
        println!("{}: {}", x, sum_squares_plus(small_data.iter().cloned(), x));
    }

    // 0..100 is more than 0..10 and I'm only using it once,
    // so I want to 'stream' it instead of storing it all in memory
    let x = 55;
    println!("{}: {}", x, sum_squares_plus(Box::new(0..100), x));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...