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 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

 

 

 

你可能感兴趣的:(Rust)