Skip to content

Rust - 迭代器和闭包

迭代器(Iterators)和闭包(Closures)是 Rust 最强大的两个特性,深受函数式编程的影响。它们允许你编写简洁、富有表达力且高效的代码来处理数据序列。

迭代器是一种模式,允许你对一系列项依次执行某些操作。在 Rust 中,迭代器是实现了标准库中 Iterator trait 的任何类型。这个 trait 的核心是 next 方法:

pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
// ... 许多其他具有默认实现的方法
}

next() 方法将序列中的每个项包装在 Some 中返回,当序列结束时,它返回 None。让我们看看如何从向量(vector)创建迭代器并手动使用它。

fn main() {
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter(); // 创建一个迭代器
// 手动使用 next()
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);
}

集合通常提供三种方法来创建迭代器,具体取决于它们如何处理数据的所有权:

MethodDescriptionItem Type
iter()创建一个迭代器,它不可变地借用每个元素。原始集合保持可用。&T
into_iter()创建一个迭代器,它获取集合及其元素的所有权。集合被消耗。T
iter_mut()创建一个迭代器,它可变地借用每个元素,允许就地修改。&mut T

迭代器的真正强大之处在于它们的适配器(adapter)方法。这些方法将一个迭代器转换为另一个。因为它们是惰性的(lazy),所以在你消费(consume)迭代器之前,它们不会产生性能开销。

fn main() {
let v1: Vec<i32> = vec![1, 2, 3];
// .map() 接受一个闭包,并创建一个迭代器,该迭代器对每个元素调用该闭包。
// .collect() 是一个消费适配器,它将结果收集到集合中。
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
println!("Original vector: {:?}", v1); // v1 仍然可用!
println!("New vector: {:?}", v2);
assert_eq!(v2, vec![2, 3, 4]);
}
Original vector: [1, 2, 3]
New vector: [2, 3, 4]

闭包是匿名函数,你可以将它们保存在变量中或作为参数传递给其他函数。与函数不同,闭包可以从它们定义的作用域中捕获值。

闭包的定义非常轻量,参数用竖线(|)括起来,后面是函数体。类型注解通常是可选的。

fn main() {
// 一个接受一个参数的简单闭包
let add_one = |x: u32| -> u32 { x + 1 };
println!("3 + 1 = {}", add_one(3));
// 类型注解通常是可选的,Rust 可以推断它们。
let add_two = |x| x + 2;
println!("10 + 2 = {}", add_two(10));
}

闭包可以从其周围作用域中“借用”或“获取”变量。Rust 根据变量在闭包内部的使用方式来推断如何执行此操作,选择不可变借用、可变借用或获取所有权。这由 Fn、FnMut 和 FnOnce trait 决定。

fn main() {
let list = vec![1, 2, 3];
println!("Before defining closure: {:?}", list);
// 此闭包不可变地借用了 `list`(Fn trait)。
let prints_list = || println!("From closure: {:?}", list);
prints_list();
println!("After calling closure: {:?}", list); // `list` 仍然可用
}

当你将迭代器和闭包结合起来时,真正的魔法就会发生。大多数迭代器适配器,如 map、filter 和 fold,都将闭包作为参数来定义其行为。

#[derive(Debug, PartialEq)]
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter() // 获取向量的所有权
.filter(|s| s.size == shoe_size) // 使用闭包按大小筛选
.collect() // 将结果收集到一个新向量中
}
fn main() {
let shoes = vec![
Shoe { size: 10, style: String::from("sneaker") },
Shoe { size: 13, style: String::from("sandal") },
Shoe { size: 10, style: String::from("boot") },
];
let my_size_shoes = shoes_in_my_size(shoes, 10);
println!("Shoes in my size: {:?}", my_size_shoes);
}
Shoes in my size: [Shoe { size: 10, style: "sneaker" }, Shoe { size: 10, style: "boot" }]