iterator在Rust或者Pytho等多种语言里都只能做一次迭代。比如在Rust中迭代到容器末尾后就会返回一个None此时再继续遍历也没有意义了,同时也没有提供方法可以重置。只能用之即弃了。
这个特性在std::io::Lines表现十分典型。来看下面代码:
fn compare(src : &str, des : &str){
let f = File::open(src);
let mut f = match f {
Ok(file) => file,
Err(e) => return,
};
let f_des = File::open(des);
let mut f_des = match f_des {
Ok(file) => file,
Err(e) => return,
};
let buf_src = BufReader::new(&f).lines();
for src_line in buf_src{
let src = src_line.unwrap();
let buf_des = BufReader::new(&f_des).lines();
for des_line in buf_des{
//比较每行内容
print!("src line: {} ", src);
println!("des line: {}", &des_line.unwrap());
}
}
}
上面代码打开了两个文件,并且比较文件每行内容的差异。分别读取两个文件后,通过for循环嵌套,实现逐行对比。感觉很正常,编译通过,运行... 然鹅出问题了。
src line: src0 des line: des0
src line: src0 des line: des1
src line: src0 des line: des2
src line: src0 des line: des3
和期望结果不同,内层的循环只循环了一次。
为了找出原因,我们首先来查看Lines的定义:
impl Iterator for Lines
原来实现的是Iterator接口。
这意味着内层循环在迭代器移动容器末尾后,当外层循环在执行到内层时,迭代器已是末尾,所以不再执行内层循环。
如何解决这个问题?
可以通过Vec来间接避免迭代器用之即弃的问题。修改下代码:
fn compare1(src : &str, des : &str){
let f = File::open(src);
let mut f = match f {
Ok(file) => file,
Err(e) => return,
};
let f_des = File::open(des);
let mut f_des = match f_des {
Ok(file) => file,
Err(e) => return,
};
let buf_des = BufReader::new(&f_des).lines();
let buf_src = BufReader::new(&f).lines();
let mut vec_des = Vec::new();
for des_line in buf_des{
vec_des.push(des_line.unwrap());
}
for src_line in buf_src{
let src = src_line.unwrap();
//print!("src line: {} ", src);
for des_line in &vec_des{
//比较每行内容
print!("src line: {} ", src);
println!("des line: {}", &des_line);
}
}
}
为什么使用了vec就解决了问题,吃个鸭脖,我们继续来分析问题。
=======================================路过的分割线=============================================
为了找到原因,还是需要从定义出发,找到vec定义,与迭代器相关Vec实现了trait IntoIterator:
impl IntoIterator for Vec
继续查找IntoIterator就会发现这个东东是对Iterator的包装。
pub trait IntoIterator where Self::IntoIter::Item == Self::Item { type Item; type IntoIter: Iterator; fn into_iter(self) -> Self::IntoIter; }
for循环中通过intoIterator返回一个迭代器,对Vec的循环等价于
let result = match IntoIterator::into_iter(&v) {
mut iter => loop {
match iter.next() {
Some(x) => { print!("{}", x); }
None => break,
}
},
};
这样保证了即使外层嵌套了循环,内层执行时都是重新生成一个迭代器,从而避免了迭代器用之即弃的问题。
以上个人观点,如果有错误请不吝指教。欢迎讨论,共同学习。
参考资料:
https://hardocs.com/d/rustprimer/iterator/iterator.html
https://www.rust-lang.org/learn