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: BufRead> Iterator for Lines<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> IntoIterator for Vec<T>

繼續查找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

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章