疊代器和擁有權

Rust 的擁有權模型會影響許多 API。IteratorIntoIterator 特徵就是一例。

Iterator

特徵就像介面一樣,可以說明型別的行為 (方法)。Iterator 特徵就是指您可以呼叫 next,直到取回 None 為止:

#![allow(unused)]
fn main() {
pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
}
}

您可以像下方這樣使用這個特徵:

fn main() {
    let v: Vec<i8> = vec![10, 20, 30];
    let mut iter = v.iter();

    println!("v[0]: {:?}", iter.next());
    println!("v[1]: {:?}", iter.next());
    println!("v[2]: {:?}", iter.next());
    println!("No more items: {:?}", iter.next());
}

如要瞭解疊代器傳回的型別為何,不妨在這裡測試答案:

fn main() {
    let v: Vec<i8> = vec![10, 20, 30];
    let mut iter = v.iter();

    let v0: Option<..> = iter.next();
    println!("v0: {v0:?}");
}

思考一下,為什麼會使用這種型別?

IntoIterator

Iterator 特徵會告訴您如何在建立疊代器後進行「疊代」。相關特徵 IntoIterator 則會說明如何建立疊代器:

#![allow(unused)]
fn main() {
pub trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;

    fn into_iter(self) -> Self::IntoIter;
}
}

這裡的語法表示每個 IntoIterator 的實作都必須宣告兩種型別:

  • Item:進行疊代的型別,例如 i8
  • IntoIterinto_iter 方法傳回的 Iterator 型別。

請注意,IntoIterItem 已建立連結:疊代器必須具有相同的 Item 型別,表示會傳回 Option<Item>

和先前一樣,思考疊代器傳回的型別為何。

fn main() {
    let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
    let mut iter = v.into_iter();

    let v0: Option<..> = iter.next();
    println!("v0: {v0:?}");
}

for 迴圈

現在我們已瞭解 IteratorIntoIterator,可以建構 for 迴圈了。這會在運算式上呼叫 into_iter(),並對產生的疊代器進行疊代:

fn main() {
    let v: Vec<String> = vec![String::from("foo"), String::from("bar")];

    for word in &v {
        println!("word: {word}");
    }

    for word in v {
        println!("word: {word}");
    }
}

思考一下,每個迴圈中的 word 型別為何?

請用上方的程式碼進行試驗,並參閱 impl IntoIterator for &Vec<T>impl IntoIterator for Vec<T> 的說明文件確認答案。