用Iterator解釋Rust所有權概念

banq發表於2022-10-07

當涉及到集合中元素的所有權時,迭代器起著極其重要的作用。
在下面這些例子中,我們將使用Vec<String>,故意使用String作為元素(它沒有實現Copy trait:String預設是值傳遞,不是引用傳遞,也不是值複製),這樣我們就可以在向量中演示其移動語義。

讓我們從一個對names進行迭代的for-loop開始。為什麼是for-loop?我們後面將討論這個問題。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
    for name in names {
        println!("{}", name);
    }
}


但是,當直到我們在for-loop下面新增一個print語句......在那裡編譯器開始抱怨。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
  
    for name in names {
        println!("{}", name);
    }
  
    println!("Names: {:?}", names);
}

編譯器:

error[E0382]: borrow of moved value: `names`
 --> src/main.rs:11:29


顯然,在迴圈之前有一個 "隱含的.into_iter呼叫"。

等等,隱式?為什麼呢!?但是,好吧,讓我們新增那個隱式的.into_iter。

在加入into_iter()之後,我們預計程式仍然不能編譯。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    for name in names.into_iter() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}


error[E0382]: borrow of moved value: `names`
 --> src/main.rs:11:29


嗯,讓我們給names.into_iter()分配一個變數。這樣我們就可以直觀地看到這個動作。請注意,我們仍然希望程式不被編譯。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
  
    let names_iter = names.into_iter();
    for name in names_iter {
        println!("{}", name);
    }
    println!("Names: {:?}", names);
}


error[E0382]: borrow of moved value: `names`
 --> src/main.rs:11:29


發生的情況是,使用 into_iter,我們正在將元素從 names 移到 names_iter。結果是,我們不能再使用names了!

我們該怎麼做呢?

我們可以使用.iter()從名字中借用元素:

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    for name in names.iter() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}


正常輸出:

John
Jane
Names: ["John", "Jane"]


我們可以使用一個slice(實現了IntoIterator):

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    for name in &names {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}

正常輸出

如果你的程式允許,我們可以把程式碼行調換一下,先借用名字,然後再移動它。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    println!("Names: {:?}", names);

    for name in names {
        println!("{}", name);
    }
}


我們也可以克隆該vector。請注意,克隆是有成本的。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    for name in names.clone() {
        println!("{}", name);
    }

    println!("Names: {:?}", names);
}



函數語言程式設計
使用函數語言程式設計成語使得當你想對元素進行迭代時,迭代器的作用更加明顯(尤其是當你來自於不用直接處理迭代器的程式語言時)。

與for-loop不同,沒有隱含的.into_iter()呼叫。因此,對於向量,我們總是需要呼叫.iter()、.into_iter()等來獲得一個迭代器。

下面程式碼不能編譯,因為我們需要對元素進行迭代,這意味著我們需要使用例如.iter()獲得一個迭代器。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
    
    names.for_each(|name|
          println!("{}", name));
    
    println!("{:?}", names);
}


error[E0599]: `Vec<String>` is not an iterator
 --> src/main.rs:7:11



這裡,我們使用了.iter()和.for_each()的組合。

迭代器返回一個元素的引用。我們仍然可以在之後使用names物件。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
    
    names
        .iter()
        .for_each(|name|
              println!("{}", name));
    
    println!("{:?}", names);
}


輸出:

John
Jane
<p class="indent">["John", "Jane"]



如果你想移動這些元素,可以使用.into_iter()。然而,請注意,之後我們就不能使用這個向量了。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
  
    names
        .into_iter()
        .for_each(|name|
              println!("{}", name));
  
    println!("{:?}", names); 
}


error[E0382]: borrow of moved value: `names`
 --> src/main.rs:12:22


你需要重新設計你的程式,使你不使用被移動的元素。這裡,我們把列印語句移到了前面。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];
    
    println!("{:?}", names); 

    names
        .into_iter()
        .for_each(|name|
              println!("{}", name));
}



我們也可以在呼叫.into_iter()之前克隆該向量。

fn main() {
    let names = vec![
        String::from("John"),
        String::from("Jane"),
    ];

    names.clone()
        .into_iter()
        .for_each(|name|
              println!("{}", name));
    
    println!("{:?}", names); 
}


 

相關文章