Rust进阶-用之即弃的iterator
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<B: > for <B>
原来实现的是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<T> for <T>
继续查找IntoIterator就会发现这个东东是对Iterator的包装。
pub trait IntoIterator where Self:::: == Self:: { type Item; type IntoIter: ; fn (self) -> Self::; }
for循环中通过intoIterator返回一个迭代器,对Vec的循环等价于
let result = match IntoIterator::into_iter(&v) { mut iter => loop { match iter.next() { Some(x) => { print!("{}", x); } None => break, } }, };
这样保证了即使外层嵌套了循环,内层执行时都是重新生成一个迭代器,从而避免了迭代器用之即弃的问题。
以上个人观点,如果有错误请不吝指教。欢迎讨论,共同学习。
参考资料: