从一个向量多次使用迭代器作为函数的参数

时间:2019-06-05 19:49:37

标签: rust iterator ownership

我正在尝试编写一些Rust代码来解码来自SDR接收器的GPS数据。我正在从文件中读取样本,并将二进制数据转换为一系列复数,这是一个耗时的过程。但是,有时候我想不带采样就将其流式传输而不保留在内存中(例如,一个非常大的文件仅以一种方式处理,或者直接从接收器中采样),而另一些时候,我想将整个数据集保留在内存中(例如,一个以多种不同方式处理的小文件),以避免重复解析二进制文件的工作。

因此,我想用迭代器编写尽可能通用的函数或结构,但是我知道它们的大小不大,因此我需要将它们放在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 个答案:

答案 0 :(得分:2)

您正在遇到的当前错误在这里:

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

您要声明要返回一个usize个项目的迭代器,但是small_data.iter()是一个返回对usize个项目(&usize)的引用的迭代器。那就是为什么您得到错误“期望的参考,找到了使用”的原因。 usize是可克隆的小型类型,因此您可以简单地使用.cloned()迭代器适配器来提供实际上返回usize的迭代器。

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));
}