【问题标题】:How can I have multiple iterators to the same data pertaining to a file?如何对与文件有关的相同数据有多个迭代器?
【发布时间】:2020-05-26 08:40:02
【问题描述】:

我有一个文件,我希望读取并将数据过滤到两个不同的集合中,并确定每个集合中的项目数。

use std::io::{self, BufRead};

fn main() {
    let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");

    let lines = cursor.lines().map(|l| l.unwrap());

    let soft_count = lines.filter(|line| line.contains("pillow")).count();

    let hard_count = lines.filter(|line| !line.contains("pillow")).count();
}

但是,借用检查器给了我一个错误:

error[E0382]: use of moved value: `lines`
  --> src/main.rs:14:22
   |
8  |     let lines = cursor.lines().map(|l| l.unwrap());
   |         ----- move occurs because `lines` has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure@src/main.rs:8:36: 8:50]>`, which does not implement the `Copy` trait
9  |     
10 |     let soft_count = lines
   |                      ----- value moved here
...
14 |     let hard_count = lines
   |                      ^^^^^ value used here after move

我尝试使用引用计数来解决这个问题以允许多重所有权:

use std::io::{self, BufRead};
use std::rc::Rc;

fn main() {
    let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");

    let lines = Rc::new(cursor.lines().map(|l| l.unwrap()));

    let soft_count = Rc::clone(&lines)
        .filter(|line| line.contains("pillow"))
        .count();

    let hard_count = Rc::clone(&lines)
        .filter(|line| !line.contains("pillow"))
        .count();
}

我收到类似的错误消息:

error[E0507]: cannot move out of an `Rc`
  --> src/main.rs:11:22
   |
11 |     let soft_count = Rc::clone(&lines)
   |                      ^^^^^^^^^^^^^^^^^ move occurs because value has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure@src/main.rs:9:44: 9:58]>`, which does not implement the `Copy` trait

error[E0507]: cannot move out of an `Rc`
  --> src/main.rs:15:22
   |
15 |     let hard_count = Rc::clone(&lines)
   |                      ^^^^^^^^^^^^^^^^^ move occurs because value has type `std::iter::Map<std::io::Lines<std::io::Cursor<&[u8; 19]>>, [closure@src/main.rs:9:44: 9:58]>`, which does not implement the `Copy` trait

【问题讨论】:

    标签: file-io rust iterator


    【解决方案1】:

    你不能。相反,您将需要克隆迭代器或它的一些构建块。在这种情况下,您可以克隆的最高值是Cursor

    use std::io::{self, BufRead};
    
    fn main() {
        let cursor = io::Cursor::new(b"pillow\nbrick\r\nphone");
    
        let lines = cursor.clone().lines().map(|l| l.unwrap());
        let lines2 = cursor.lines().map(|l| l.unwrap());
    
        let soft_count = lines.filter(|line| line.contains("pillow")).count();
    
        let hard_count = lines2.filter(|line| !line.contains("pillow")).count();
    }
    

    对于实际的File,您需要使用try_clone,因为它可能会失败。无论哪种情况,您都将引用相同的数据两次,并且只会保留迭代器信息。


    对于您的具体情况,您不需要任何这些。事实上,对数据进行两次迭代是低效的。您可以做的最简单的内置操作是 partition 迭代器:

    let (softs, hards): (Vec<_>, Vec<_>) = lines.partition(|line| line.contains("pillow"));
    
    let soft_count = softs.len();
    let hard_count = hards.len();
    

    这仍然有点低效,因为您不需要实际值。您可以创建自己的类型来实现 Extend 并丢弃值:

    #[derive(Debug, Default)]
    struct Count(usize);
    
    impl<T> std::iter::Extend<T> for Count {
        fn extend<I>(&mut self, iter: I)
        where
            I: IntoIterator,
        {
            self.0 += iter.into_iter().count();
        }
    }
    
    let (softs, hards): (Count, Count) = lines.partition(|line| line.contains("pillow"));
    
    let soft_count = softs.0;
    let hard_count = hards.0;
    

    您也可以只使用for 循环或在fold 之上构建一些东西:

    let (soft_count, hard_count) = lines.fold((0, 0), |mut state, line| {
        if line.contains("pillow") {
            state.0 += 1;
        } else {
            state.1 += 1;
        }
        state
    });
    

    【讨论】:

    • 感谢@Shepmaster 如此周到的回复。还有一个问题。假设您确实需要收集这些集合,并且它们不是互斥的,您的第一个建议是复制数据并迭代两次吗?我决定确定每组大小的原因是为了避免碎片化并一次性分配内存。收集方法会这样做吗?还是以单次增量请求内存?
    • FromIterator 的实现者决定是否使用迭代器的size_hint 来预分配内存。我通常假设标准库中的实现尽可能智能,因为它可以基于它所拥有的信息。如果您有没有的信息,您也许可以提供帮助,但是在您进行基准测试之前,我不会担心它。阅读this wonderful answer 了解具体示例。
    • 双重迭代与复制数据的类似想法。我认为这将归结为重叠有多大以及复制有多昂贵。基准测试是了解哪个更适合您的数据的最佳工具。
    猜你喜欢
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 2019-01-13
    • 2018-04-07
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 2011-05-09
    相关资源
    最近更新 更多