1 Vector
https://doc.rust-lang.org/std/vec/struct.Vec.html
2 迭代器简介
迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。在 Rust 中,迭代器是惰性的(lazy),这意味着在调用方法使用迭代器之前它都不会有效果:
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
上述这段代码本身并没有任何用处。
使用 for
循环使用迭代器:
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
迭代器都实现了一个叫做 Iterator
的定义于标准库的 trait:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// 此处省略了方法的默认实现
}
这段代码表明实现 Iterator
trait 要求同时定义一个 Item
类型,这个 Item
类型将是迭代器返回元素的类型;next
是 Iterator 实现者被要求定义的唯一方法,一次返回迭代器中的一个项,封装在 Some
中,当迭代器结束时,它返回 None
:
#[test]
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
assert_eq!(v1_iter.next(), Some(&1));
assert_eq!(v1_iter.next(), Some(&2));
assert_eq!(v1_iter.next(), Some(&3));
assert_eq!(v1_iter.next(), None);
}
注意 v1_iter
需要是可变的,因为在迭代器上调用 next
方法改变了迭代器中用来记录序列位置的状态。使用 for
循环时无需使 v1_iter
可变因为 for
循环会获取 v1_iter
的所有权并在后台使 v1_iter
可变。
另外需要注意到从 next
调用中得到的值是 vector 的不可变引用。iter
方法生成一个不可变引用的迭代器,如果需要一个获取 v1
所有权并返回拥有所有权的迭代器,则可以调用 into_iter
;如果希望迭代可变引用,可以调用 iter_mut
。
3 消费迭代器
Iterator trait
有一系列不同的由标准库提供默认实现的方法,其中一些方法调用了 next
方法,这些方法被称为消费适配器(consuming adaptors),因为调用他们会消耗迭代器。
#[test]
fn iterator_sum() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
}
4 迭代器适配器
Iterator
trait 中定义了另一类方法,允许将当前迭代器变为不同类型的迭代器,例如下面的 map
方法使用闭包来调用每个元素以生成新的迭代器,调用 collect
方法消费新迭代器并创建一个 Vector:
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
5 性能
通过将阿瑟·柯南·道尔的“福尔摩斯探案集”的全部内容加载进 String 并寻找其中的单词 “the”,如下是 for
循环版本和迭代器版本的 search
函数的性能测试结果:
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
迭代器作为一个高级的抽象,被编译成了与手写的底层代码大体一致性能代码。迭代器是 Rust 的零成本抽象(zero-cost abstractions)之一,它意味着抽象并不会引入运行时开销。
网友评论