如何使用同一个迭代器两次,一次用于计数,一次用于迭代?

How to use the same iterator twice, once for counting and once for iteration?

好像计数的时候消耗了一个迭代器。如何使用相同的迭代器进行计数然后对其进行迭代?

我正在尝试计算文件中的行数然后打印它们。我能够读取文件内容,我能够计算行数,但是我无法再遍历这些行,就好像内部光标位于迭代器的末尾一样。

use std::fs::File;
use std::io::prelude::*;

fn main() {
    let log_file_name = "/home/myuser/test.log";
    let mut log_file = File::open(log_file_name).unwrap();
    let mut log_content: String = String::from("");
    //Reads the log file.
    log_file.read_to_string(&mut log_content).unwrap();
    //Gets all the lines in a Lines struct.
    let mut lines = log_content.lines();
    //Uses by_ref() in order to not take ownership
    let count = lines.by_ref().count();
    println!("{} lines", count); //Prints the count
                                 //Doesn't enter in the loop
    for value in lines {
        println!("{}", value);
    }
}

Iterator doesn't have a reset method, but it seems the internal cursor is at the end of the iterator after the count. Is it mandatory to create a new Lines 通过再次调用 log_content.lines() 或者我可以重置内部光标吗?

目前,我找到的解决方法是创建一个新的迭代器:

use std::fs::File;
use std::io::prelude::*;

fn main() {
    let log_file_name = "/home/myuser/test.log";
    let mut log_file = File::open(log_file_name).unwrap();
    let mut log_content: String = String::from("");
    //Reads the log file.
    log_file.read_to_string(&mut log_content).unwrap();
    //Counts all and consume the iterator
    let count = log_content.lines().count();
    println!("{} lines", count);
    //Creates a pretty new iterator
    let lines = log_content.lines();
    for value in lines {
        println!("{}", value);
    }
}

调用 count 会消耗迭代器,因为它实际上会迭代直到完成(即 next() returns None)。

您可以通过使用 by_ref 来阻止使用迭代器,但迭代器仍然会被驱动完成(by_ref 实际上只是 returns 对迭代器的可变引用,并且 Iterator 也为可变引用实现:impl<'a, I> Iterator for &'a mut I).

如果迭代器包含您希望在完成后重用的其他状态,这仍然有用,但在这种情况下不是。

您可以简单地尝试分叉迭代器(如果没有副作用,他们通常会实现 Clone),尽管在这种情况下重新创建它同样好(大多数时候创建迭代器是便宜;真正的工作通常只有在您通过直接或间接调用 next 来驱动它时才能完成。

所以不,(在这种情况下)你不能重置它,是的,你需要创建一个新的(或在使用前克隆它)。

迭代器通常不能被迭代两次,因为它们的迭代可能会产生成本。在str::lines的情况下,每次迭代都需要找到下一个行尾,这意味着扫描整个字符串,这是有一定成本的。你可能会争辩说迭代器可以保存这些位置供以后重用,但存储它们的成本会更大。

有些 Iterator 的迭代成本甚至更高,因此您真的不想重复两次。

许多迭代器可以很容易地重新创建(这里调用 str::lines 第二次)或者是 cloned。无论你用哪种方式重新创建一个迭代器,这两个迭代器通常是完全独立的,所以迭代将意味着你将付出两次代价。

在您的特定情况下,将字符串迭代两次可能没问题,因为适合内存的字符串不应该太长以至于仅仅计算行数将是一项非常昂贵的操作。如果您认为是这种情况,首先对其进行基准测试,其次,编写您自己的算法,因为 Lines::count 可能没有得到尽可能多的优化,因为 Lines 的主要目标是迭代行。

其他答案已经很好地解释了您可以重新创建迭代器或克隆它。

如果迭代操作过于昂贵或不可能多次执行(例如从网络套接字读取),另一种解决方案是创建迭代器值的集合,这样您就可以获取长度和价值观。

这确实需要存储迭代器中的每个值; 天下没有免费的午餐

use std::fs;

fn main() {
    let log_content = fs::read_to_string("/home/myuser/test.log").unwrap();
    let lines: Vec<_> = log_content.lines().collect();

    println!("{} lines", lines.len());
    for value in lines {
        println!("{}", value);
    }
}