迭代器

迭代器是一个构造,它可以给你集合中的元素,一次一个。实际上,我们已经使用了很多迭代器:for循环给你一个迭代器。当你想在其他时候使用迭代器时,你必须选择什么样的迭代器:

  • .iter() 引用的迭代器
  • .iter_mut() 可变引用的迭代器
  • .into_iter() 值的迭代器(不是引用)

for循环其实只是一个拥有值的迭代器。这就是为什么可以让它变得可变,然后你可以在使用的时候改变值。

我们可以这样使用迭代器。

fn main() {
    let vector1 = vec![1, 2, 3]; // we will use .iter() and .into_iter() on this one
    let vector1_a = vector1.iter().map(|x| x + 1).collect::<Vec<i32>>();
    let vector1_b = vector1.into_iter().map(|x| x * 10).collect::<Vec<i32>>();

    let mut vector2 = vec![10, 20, 30]; // we will use .iter_mut() on this one
    vector2.iter_mut().for_each(|x| *x +=100);

    println!("{:?}", vector1_a);
    println!("{:?}", vector2);
    println!("{:?}", vector1_b);
}

这个将打印:

[2, 3, 4]
[110, 120, 130]
[10, 20, 30]

前两个我们用了一个叫.map()的方法。这个方法可以让你对每一个元素做一些事情,然后把它传递下去。最后我们用的是一个叫.for_each()的方法。这个方法只是让你对每一个元素做一些事情。.iter_mut()加上for_each()基本上就是一个for的循环。在每一个方法里面,我们可以给每一个元素起一个名字(我们刚才叫它 x),然后用它来改变它。这些被称为闭包,我们将在下一节学习它们。

让我们再来看看它们,一次一个。

首先我们用.iter()vector1进行引用。我们给每个元素都加了1,并使其成为一个新的Vec。vector1还活着,因为我们只用了引用:我们没有按值取。现在我们有 vector1,还有一个新的 Vec 叫 vector1_a。因为.map()只是传递了它,所以我们需要使用.collect()把它变成一个Vec

然后我们用into_itervector1中按值得到一个迭代器。这样就破坏了vector1,因为这就是into_iter()的作用。所以我们做了vector1_b之后,就不能再使用vector1了。

最后我们在vector2上使用.iter_mut()。它是可变的,所以我们不需要使用.collect()来创建一个新的Vec。相反,我们用可变引用改变同一Vec中的值。所以vector2仍然存在。因为我们不需要一个新的Vec,我们使用for_each:它就像一个for循环。

迭代器如何工作

迭代器的工作原理是使用一个叫做 .next() 的方法,它给出一个 Option。当你使用迭代器时,Rust会一遍又一遍地调用next()。如果得到 Some,它就会继续前进。如果得到 None,它就停止。

你还记得 assert_eq! 宏吗?在文档中,你经常看到它。这里它展示了迭代器的工作原理。

fn main() {
    let my_vec = vec!['a', 'b', '거', '柳']; // Just a regular Vec

    let mut my_vec_iter = my_vec.iter(); // This is an Iterator type now, but we haven't called it yet

    assert_eq!(my_vec_iter.next(), Some(&'a'));  // Call the first item with .next()
    assert_eq!(my_vec_iter.next(), Some(&'b'));  // Call the next
    assert_eq!(my_vec_iter.next(), Some(&'거')); // Again
    assert_eq!(my_vec_iter.next(), Some(&'柳')); // Again
    assert_eq!(my_vec_iter.next(), None);        // Nothing is left: just None
    assert_eq!(my_vec_iter.next(), None);        // You can keep calling .next() but it will always be None
}

为自己的struct或enum实现Iterator并不难。首先我们创建一个书库,想一想。

#[derive(Debug)] // we want to print it with {:?}
struct Library {
    library_type: LibraryType, // this is our enum
    books: Vec<String>, // list of books
}

#[derive(Debug)]
enum LibraryType { // libraries can be city libraries or country libraries
    City,
    Country,
}

impl Library {
    fn add_book(&mut self, book: &str) { // we use add_book to add new books
        self.books.push(book.to_string()); // we take a &str and turn it into a String, then add it to the Vec
    }

    fn new() -> Self { // this creates a new Library
        Self {
            library_type: LibraryType::City, // most are in the city so we'll choose City
                                             // most of the time
            books: Vec::new(),
        }
    }
}

fn main() {
    let mut my_library = Library::new(); // make a new library
    my_library.add_book("The Doom of the Darksword"); // add some books
    my_library.add_book("Demian - die Geschichte einer Jugend");
    my_library.add_book("구운몽");
    my_library.add_book("吾輩は猫である");

    println!("{:?}", my_library.books); // we can print our list of books
}

这很好用。现在我们想为库实现Iterator,这样我们就可以在for循环中使用它。现在如果我们尝试 for 循环,它就无法工作。


#![allow(unused)]
fn main() {
for item in my_library {
    println!("{}", item); // ⚠️
}
}

它说:

error[E0277]: `Library` is not an iterator
  --> src\main.rs:47:16
   |
47 |    for item in my_library {
   |                ^^^^^^^^^^ `Library` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `Library`
   = note: required by `std::iter::IntoIterator::into_iter`

但是我们可以用impl Iterator for Library把库做成迭代器。Iteratortrait的信息在标准库中。https://doc.rust-lang.org/std/iter/trait.Iterator.html

在页面的左上方写着:Associated Types: ItemRequired Methods: next。"关联类型"的意思是 "一起使用的类型"。我们的关联类型将是String,因为我们希望迭代器给我们提供String。

在页面中,它有一个看起来像这样的例子。

// an iterator which alternates between Some and None
struct Alternate {
    state: i32,
}

impl Iterator for Alternate {
    type Item = i32;

    fn next(&mut self) -> Option<i32> {
        let val = self.state;
        self.state = self.state + 1;

        // if it's even, Some(i32), else None
        if val % 2 == 0 {
            Some(val)
        } else {
            None
        }
    }
}

fn main() {}

你可以看到impl Iterator for Alternate下面写着type Item = i32。这就是关联类型。我们的迭代器将针对我们的书籍列表,这是一个Vec<String>。当我们调用next的时候。 它将给我们一个String。所以我们就写type Item = String;。这就是关联项。

为了实现 Iterator,你需要写 fn next() 函数。这是你决定迭代器应该做什么的地方。对于我们的 Library,我们首先希望它给我们最后一本书。所以我们将match.pop()一起,如果是Some的话,就把最后一项去掉。我们还想为每个元素打印 "is found!"。现在它看起来像这样:

#[derive(Debug, Clone)]
struct Library {
    library_type: LibraryType,
    books: Vec<String>,
}

#[derive(Debug, Clone)]
enum LibraryType {
    City,
    Country,
}

impl Library {
    fn add_book(&mut self, book: &str) {
        self.books.push(book.to_string());
    }

    fn new() -> Self {
        Self {
            library_type: LibraryType::City,
            // most of the time
            books: Vec::new(),
        }
    }
}

impl Iterator for Library {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        match self.books.pop() {
            Some(book) => Some(book + " is found!"), // Rust allows String + &str
            None => None,
        }
    }
}

fn main() {
    let mut my_library = Library::new();
    my_library.add_book("The Doom of the Darksword");
    my_library.add_book("Demian - die Geschichte einer Jugend");
    my_library.add_book("구운몽");
    my_library.add_book("吾輩は猫である");

    for item in my_library.clone() { // we can use a for loop now. Give it a clone so Library won't be destroyed
        println!("{}", item);
    }
}

这个打印:

吾輩は猫である is found!
구운몽 is found!
Demian - die Geschichte einer Jugend is found!
The Doom of the Darksword is found!