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

Ant*_*nyB 3 iterator reset rust

似乎在计数时消耗了迭代器.我如何使用相同的迭代器进行计数然后迭代它?

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Iterator没有reset方法,但似乎内部游标在计数后位于迭代器的末尾.是否必须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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*fan 7

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

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

如果迭代器在完成后包含您想要重用的其他状态,那么这仍然很有用,但在这种情况下不是这样.

您可以简单地尝试分析迭代器(Clone如果它们没有副作用,它们通常会实现),虽然在这种情况下重新创建它同样好(大多数时候创建迭代器很便宜;实际工作通常只在你next直接或间接打电话来驾驶它.

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


She*_*ter 7

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

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


mca*_*ton 5

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

有些Iterator迭代的成本甚至更高,所以你真的不想重复两次。

许多迭代器可以轻松地重新创建(此处称为str::lines第二次调用)或被clone删除。无论您以哪种方式重新创建迭代器,这两个迭代器通常都是完全独立的,因此迭代意味着您将付出两次代价。

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