Rust 常用语法速记 - 迭代器
Rust 的迭代器(Iterator)是一种强大而高效的数据处理工具 ,它允许以声明式的方式遍历和操作集合中的元素。迭代器是 Rust 零成本抽象的典范,意味着它在提供高级抽象的同时几乎不产生运行时开销。
1. 迭代器基础概念
什么是迭代器?
迭代器是任何实现了 Iteratortrait 的类型,它能够逐个产生序列中的值。迭代器的核心特点是惰性求值(lazy evaluation),只有在消费时才会真正执行计算 。
rust
// Iterator trait 基本定义
pub trait Iterator {
type Item; // 关联类型:迭代器产生的元素类型
fn next(&mut self) -> Option<Self::Item>; // 核心方法
}
创建迭代器的三种方式
根据所有权需求,有三种创建迭代器的方法 :
| 方法 | 元素类型 | 所有权 | 原集合后续可用性 |
|---|---|---|---|
iter() |
&T(不可变引用) |
借用 | 是 |
iter_mut() |
&mut T(可变引用) |
可变借用 | 是 |
into_iter() |
T(值本身) |
转移所有权 | 否 |
示例代码:
rust
let vec = vec![1, 2, 3];
// 不可变借用,vec 仍可用
for x in vec.iter() {
println!("{}", x);
}
let mut vec = vec![1, 2, 3];
// 可变借用,可修改元素
for mut x in vec.iter_mut() {
*x *= 2;
}
// 获取所有权,vec 不再可用
for x in vec.into_iter() {
println!("{}", x);
}
2. 消费适配器
消费适配器会消耗迭代器并返回最终结果,调用后迭代器无法再使用 。
collect()
将迭代器元素收集到集合中:
rust
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
println!("{:?}", doubled);// [2, 4, 6]
collect::<Vec<_>>()
显式指定将迭代器元素收集到 Vec中
rust
// 从范围创建向量
let numbers = (1..5).collect::<Vec<_>>();
println!("{:?}", numbers); // [1, 2, 3, 4]
// 过滤后收集
let even_numbers = (1..10).filter(|x| x % 2 == 0).collect::<Vec<_>>();
println!("{:?}", even_numbers); // [2, 4, 6, 8]
何时需要使用该语法
- 编译器无法推断类型时
- 代码清晰性:明确指定集合类型增强可读性
- 复杂链式操作:中间步骤类型信息可能丢失时
sum() 和 product()
对元素进行求和或求积:
rust
let v = vec![1, 2, 3, 4];
let total: i32 = v.iter().sum(); // 总和:10
let product: i32 = v.iter().product(); // 乘积:24
fold()
自定义累积操作(功能最强大),它允许你通过一个累积操作,将整个序列归约为一个单一的值。
fold()的基本语法是 iterator.fold(init, |acc, x| {...})。它接受两个参数:一个初始值 init,以及一个接收当前累积值 acc和迭代元素 x的闭包。闭包返回的值将成为下一次迭代的 acc。
rust
// 简单一点的
let v = vec![1, 2, 3, 4];
let sum = v.iter().fold(0, |acc, x| acc + x); // 累加:10
let product = v.iter().fold(1, |acc, x| acc * x); // 累乘:24
// 复杂一点的
// 拼接字符串
let words = vec!["Rust", "is", "powerful"];
let sentence = words.iter().fold(String::new(), |mut acc, &word| {
if !acc.is_empty() {
acc.push(' ');
}
acc.push_str(word);
acc
});
println!("{}", sentence);//Rust is powerful
// 分组统计
let numbers = vec![1, 2, 3, 4, 5, 6];
let (even, odd): (Vec<i32>, Vec<i32>) = numbers.into_iter().fold(
(Vec::new(), Vec::new()), // 初始值:两个空Vec
|(mut evens, mut odds), x| {
if x % 2 == 0 {
evens.push(x);
} else {
odds.push(x);
}
(evens, odds) // 返回更新后的元组
}
);
println!("Evens: {:?}, Odds: {:?}", even, odd); // Evens: [2, 4, 6], Odds: [1, 3, 5]
// 实现条件性累积
let numbers = vec![1, 2, 3, 4, 5];
let product_of_evens = numbers.iter().fold(1, |acc, &x| {
if x % 2 == 0 {
acc * x
} else {
acc
}
});
println!("Product of evens: {}", product_of_evens); // Product of evens: 8
try_fold()
try_fold() 是 fold() 的安全变体,用于可能失败的累积操作,当遇到错误时立即终止处理并返回错误
rust
let numbers = vec![1, 2, 3, 4, 5];
let result = numbers.iter().try_fold(0_i32, |acc, &x| {
acc.checked_add(x) // 返回 Option<i32>
.ok_or("整数加法溢出!") // 转换为 Result<i32, &str>
});
match result {
Ok(sum) => println!("总和: {}", sum), // 正常情况:总和: 15
Err(e) => println!("错误: {}", e), // 错误情况
}
let numbers = vec![1, 11, 111, 127];
let triangular = numbers.iter().try_fold(0_i8, |prev, &e| {
match prev.checked_add(e) {
Some(next) => ControlFlow::Continue(next), // 继续累积
None => ControlFlow::Break(prev), // 溢出时中断, 返回当前值
}
});
println!("{:?}", triangular); // 输出: Break(123)
// 实际应用场景
// 数据验证
let user_inputs = vec!["123", "456", "abc", "789"];
let parsed_numbers: Result<Vec<i32>,&'static str> = user_inputs.iter()
.try_fold(Vec::new(),|mut acc,&input|{
match input.parse::<i32>() {
Ok(num) => {
acc.push(num);
Ok(acc)
},
Err(_) => Err("无效数字输入")
}
});
match parsed_numbers {
Ok(numbers) => println!("成功解析的数字: {:?}", numbers),
Err(e) => println!("错误: {}", e), // 输出: 错误: 无效数字输入
}
// 文件处理管道
let files = vec!["file1.txt", "file2.txt", "file3.txt"];
let processed_data = files.iter().try_fold(String::new(), |acc, &filename| {
std::fs::read_to_string(filename)
.map(|content| acc + &content)
.map_err(|_| format!("无法读取文件: {}", filename))
});
match processed_data {
Ok(data) => println!("处理后的数据: {}", data),
Err(e) => println!("错误: {}", e), // 输出: 错误: 无法读取文件: file1.txt
}
reduce()
类似fold,但使用第一个元素作为初始值
rust
// 简单数值计算
let v = vec![1, 2, 3, 4, 5];
// 求和:使用第一个元素1作为初始值, 用 copied() 把 &i32 变成 i32
let sum = v.iter().copied().reduce(|acc, x| acc + x);
println!("求和结果: {:?}", sum); //输出: 求和结果: Some(15)
// 求积:使用第一个元素1作为初始值
let product = v.iter().copied().reduce(|acc, x| acc * x);
println!("求积结果: {:?}", product); //输出: 求积结果: Some(120)
// 查找最大值
let max = v.iter().copied().reduce(|acc, x| if acc > x { acc } else { x });
println!("最大值: {:?}", max); //输出: 最大值: Some(5)
// 浮点数特殊处理 (NaN 是 not comparable)
let floats = vec![2.4, f32::NAN, 1.3];
let max_float = floats.iter().copied().reduce(|a, b| a.max(b));
println!("浮点数最大值: {:?}", max_float); //输出: 浮点数最大值: Some(2.4)
// 空迭代器
let empty_vec: Vec<i32> = Vec::new();
let result = empty_vec.iter().copied().reduce(|acc, x| acc + x);
println!("空迭代器结果: {:?}", result); //输出: 空迭代器结果: None
// 字符串拼接
let words = vec!["Rust", "reduce", "方法"];
let combined = words.iter().map(|w| w.to_string()).reduce(|acc, word| {
format!("{} {}", acc, word)
});
println!("拼接结果: {:?}", combined); //输出: 拼接结果: Some("Rust reduce 方法")
fold() 和 reduce() 区别
| 特性 | fold() |
reduce() |
|---|---|---|
| 初始值 | 必须显式指定 (init: B) |
使用迭代器的第一个元素 |
| 返回值类型 | B(直接返回累积结果) |
Option<T>(空迭代器返回None) |
| 空迭代器处理 | 返回指定的初始值init |
返回None |
| 适用场景 | 更通用、更安全,尤其适合可能为空或需不同类型累积的场景 | 更简洁,专为非空且累积逻辑与元素类型一致的场景设计 |
count()、min()、max()
计算元素个数 查找最小/最大元素 。返回Option<T>。
rust
let v = vec![10, 5, 8, 20];
let count = v.iter().count(); // 4
let min = v.iter().min();// Some(5)
let max = v.iter().max();// Some(20)
any() 和 all()
条件判断:
rust
let v = vec![1, 2, 3, 4];
let has_even = v.iter().any(|x| x % 2 == 0); // 是否有偶数:true
let all_even = v.iter().all(|x| x % 2 == 0); // 是否全为偶数:false
find() 和 position()
查找元素:
find: 查找第一个 满足条件的元素,返回Option<T>
position: 查找第一个 满足条件的元素的索引,返回Option<usize>。
rust
let v = vec!["apple", "banana", "cherry"];
let found = v.iter().find(|s| s.starts_with('b')); // Some("banana")
let pos = v.iter().position(|s| *s == "banana"); // Some(1)
rposition()
从后向前查找(需双端迭代器且长度已知)。
rust
let numbers = vec![10, 20, 30, 20, 40];
// 查找最后一个等于20的元素的位置(从后向前)
let pos = numbers.iter().rposition(|&x| x == 20);
println!("最后一个20的位置: {:?}", pos); // 输出: 最后一个20的位置: Some(3)
// 查找不存在的元素
let not_found = numbers.iter().rposition(|&x| x == 99);
println!("查找99的结果: {:?}", not_found); // 输出: 查找99的结果: None
// 实际应用:查找文件扩展名的点位置
let filename = "document.backup.txt";
let chars: Vec<char> = filename.chars().collect();
let dot_pos = chars.iter().rposition(|&c| c == '.');
println!("最后一个点的位置: {:?}", dot_pos); // 输出: 最后一个点的位置: Some(15)
if let Some(pos) = dot_pos {
let extension = &filename[pos + 1..];
println!("文件扩展名: {}", extension); // 输出: 文件扩展名: txt
};
last()
获取最后一个元素 。
rust
let numbers = vec![1, 2, 3, 4, 5];
// 获取最后一个元素
let last_element = numbers.iter().last();
println!("最后一个元素: {:?}", last_element); // 输出: 最后一个元素: Some(5)
// 空序列处理
let empty_vec: Vec<i32> = vec![];
let empty_last = empty_vec.iter().last();
println!("空序列的最后一个元素: {:?}", empty_last); // 输出: 空序列的最后一个元素: None
// 实际应用:获取最新的一条记录
let logs = vec![
"2023-01-01: 系统启动",
"2023-01-02: 用户登录",
"2023-01-03: 数据备份"
];
if let Some(latest_log) = logs.iter().last() {
println!("最新日志: {}", latest_log); // 输出: 最新日志: 2023-01-03: 数据备份
}
nth()
获取指定位置元素 。
rust
let numbers = vec![10, 20, 30, 40, 50];
// 获取第0个元素(第一个)
let first = numbers.iter().nth(0);
println!("第0个元素: {:?}", first); // 输出: 第0个元素: Some(10)
// 获取第2个元素(第三个)
let third = numbers.iter().nth(2);
println!("第2个元素: {:?}", third); // 输出: 第2个元素: Some(30)
// 超出范围的索引
let out_of_bound = numbers.iter().nth(10);
println!("第10个元素: {:?}", out_of_bound); // 输出: 第10个元素: None
// 实际应用:分页查询(获取第page_size页的数据)
let page_size = 3;
let page_num = 1; // 第二页(从0开始)
let page_start = page_num * page_size;
let page_data: Vec<_> = (page_start..page_start + page_size)
.filter_map(|i| numbers.iter().nth(i))
.collect();
println!("第{}页数据: {:?}", page_num + 1, page_data); // 输出: 第2页数据: [40, 50]
// nth()会消耗之前的元素,后续操作从nth之后开始
let mut iter = numbers.iter();
let _ = iter.nth(2);
let remaining: Vec<_> = iter.collect();
println!("nth(2)后剩余元素: {:?}", remaining); // 输出: nth(2)后剩余元素: [40, 50]
eq() / ne() 和 lt() / le() / gt() / ge()
比较两个迭代器产生的序列是否相等或不等以及大小关系 。
rust
// 判断两个迭代器序列是否完全相等
let result1 = [1, 2, 3].iter().eq([1, 2, 3].iter()); // true
let result2 = [1, 2, 3].iter().eq([2, 1, 3].iter()); // false
// 按字典序比较(逐个元素比较)
let result3 = [1, 2, 3].iter().lt([1, 2, 4].iter()); // true
let result4 = [1, 2, 3].iter().gt([1, 2, 2].iter()); // true
let result5 = [1, 2, 3].iter().le([1, 2, 3].iter()); // true
let result6 = [1, 2, 3].iter().ge([1, 2, 3].iter()); // true
//实际使用案例
let version1 = vec![1, 0, 2];
let version2 = vec![1, 1, 0];
if version1.iter().lt(version2.iter()) {
println!("版本1比版本2旧"); // 版本1比版本2旧
}
// 字符串按字典序排序判断
let words = vec!["apple", "banana", "cherry"];
let search_word = "blueberry";
// 检查是否所有单词都排在搜索词之前
let all_before = words.iter().all(|word| word.lt(&search_word));
println!("所有单词都在blueberry之前: {}", all_before); // 所有单词都在blueberry之前: false
max_by_key() 和 min_by_key()
当只需要按单个字段或简单计算值比较时,代码更简洁 。
rust
let people = vec![
Person { name: "Alice".into(), age: 30, salary: 50000.0 },
Person { name: "Bob".into(), age: 25, salary: 60000.0 },
Person { name: "Charlie".into(), age: 35, salary: 45000.0 },
];
// 查找年龄最大的人
let oldest = people.iter().max_by_key(|p| p.age);
println!("最年长: {:?}", oldest.map(|p| &p.name)); // 最年长: Some("Charlie")
// 查找薪资最低的人
let lowest_paid = people.iter().min_by_key(|p| p.salary as i64);
println!("薪资最低: {:?}", lowest_paid.map(|p| &p.name)); // 薪资最低: Some("Charlie")
max_by() 和 min_by()
当需要复杂的多字段比较、自定义排序逻辑、或处理特殊比较规则时,提供一个比较闭包,直接决定两个元素的大小关系。闭包需要返回 std::cmp::Ordering(Less、Equal、Greater)。
rust
use std::cmp::Ordering;
fn main() {
let people = vec![
Person { name: "Alice".into(), age: 30, salary: 80000.0, years_of_experience: 5 },
Person { name: "Bob".into(), age: 35, salary: 90000.0, years_of_experience: 8 },
Person { name: "Charlie".into(), age: 28, salary: 90000.0, years_of_experience: 6 },
Person { name: "David".into(), age: 40, salary: 85000.0, years_of_experience: 12 },
Person { name: "Eve".into(), age: 32, salary: 90000.0, years_of_experience: 7 },
];
// 三字段复杂排序:薪资降序 → 工作年限降序 → 年龄升序
let highest_paid = people.iter().max_by(|a, b| {
a.salary.partial_cmp(&b.salary)
.unwrap_or(Ordering::Equal)
.reverse() // 薪资降序
.then_with(|| b.years_of_experience.cmp(&a.years_of_experience))
.then_with(|| a.age.cmp(&b.age))
});
println!("按复杂规则找到的最高薪资者: {:?}", highest_paid.map(|p| &p.name)); // 按复杂规则找到的最高薪资者: Some("Alice")
}
#[derive(Debug)]
struct Person {
name: String,
age: u32,
salary: f64,
years_of_experience: u32,
}
上面示例如果属性存在为空的情况,可以用下面这几个方案
方案1:为 Option<T>字段定义明确的排序规则
rust
use std::cmp::Ordering;
fn main() {
let people = vec![
Person::new("Alice", Some(30), Some(80000.0), Some(5)),
Person::new("Bob", Some(35), Some(90000.0), Some(8)),
Person::new("Charlie", Some(28), None, Some(6)), // 薪资未知
Person::new("David", Some(40), Some(85000.0), None), // 工作经验未知
Person::new("Eve", None, Some(90000.0), Some(7)), // 年龄未知
];
// 安全的三字段排序:处理空值情况
let highest_paid = people.iter().max_by(|a,b|{
// 1. 薪资比较:空值视为最小
let salary_cmp = match (a.salary,b.salary) {
(Some(s1), Some(s2)) => s1.partial_cmp(&s2).unwrap_or(Ordering::Equal),
(Some(_),None) => Ordering::Greater, // 有薪资 > 无薪资
(None,Some(_)) => Ordering::Less, // 无薪资 < 有薪资
(None,None) => Ordering::Equal,
}.reverse(); //薪资降序
// 2. 工作年限比较:空值视为最小
let exp_cmp = match (a.years_of_experience, b.years_of_experience) {
(Some(e1), Some(e2)) => e1.cmp(&e2),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
}.reverse(); // 工作年限降序
// 3. 年龄比较:空值视为最小(升序时空值排前面)
let age_cmp = match (a.age, b.age) {
(Some(a1), Some(a2)) => a1.cmp(&a2),
(Some(_), None) => Ordering::Greater, // 有年龄 > 无年龄
(None, Some(_)) => Ordering::Less, // 无年龄 < 有年龄
(None, None) => Ordering::Equal,
}; // 年龄升序(不需要reverse)
salary_cmp.then_with(|| exp_cmp).then_with(|| age_cmp)
});
println!("按安全规则找到的最高薪资者: {:?}",
highest_paid.map(|p| &p.name)); // 输出: 按安全规则找到的最高薪资者: Some("Charlie")
}
#[derive(Debug)]
struct Person {
name: String,
age: Option<u32>, // 年龄可能未知
salary: Option<f64>, // 薪资可能未知
years_of_experience: Option<u32>, // 工作经验可能未知
}
impl Person {
fn new(name: &str, age: Option<u32>, salary: Option<f64>, exp: Option<u32>) -> Self {
Person {
name: name.to_string(),
age,
salary,
years_of_experience: exp,
}
}
}
方案2:使用 Option的内置比较方法(用max即可)
Option本身实现了 Ordtrait(当 T实现 Ord时),其排序规则是:None小于任何 Some值。
rust
use std::cmp::Ordering;
fn main() {
let mut people = vec![
Person::new("Alice", Some(30), Some(80000.0), Some(5)),
Person::new("Bob", Some(35), Some(90000.0), Some(8)),
Person::new("Charlie", Some(28), None, Some(6)), // 薪资未知
Person::new("David", Some(40), Some(85000.0), None), // 工作经验未知
Person::new("Eve", None, Some(90000.0), Some(7)), // 年龄未知
];
println!("按安全规则找到的最高薪资者: {:?}",
people.iter().max().unwrap().name); // 按安全规则找到的最高薪资者: "Charlie"
}
#[derive(Debug)]
struct Person {
name: String,
age: Option<u32>,
salary: Option<f64>,
years_of_experience: Option<u32>,
}
impl Person {
fn new(name: &str, age: Option<u32>, salary: Option<f64>, exp: Option<u32>) -> Self {
Person {
name: name.to_string(),
age,
salary,
years_of_experience: exp,
}
}
}
// 1. 实现 PartialEq
impl PartialEq for Person {
fn eq(&self, other: &Self) -> bool {
// 定义"相等"的逻辑:所有字段都相等
self.name == other.name
&& self.age == other.age
&& self.salary == other.salary
&& self.years_of_experience == other.years_of_experience
}
}
// 2. 实现 Eq(标记 trait,无需额外方法)
impl Eq for Person {}
// 3. 实现 PartialOrd
impl PartialOrd for Person {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
// 4. 实现 Ord - 核心排序逻辑
impl Ord for Person {
fn cmp(&self, other: &Self) -> Ordering {
// 主要排序规则:按薪资降序(高薪资排前面)
let salary_ordering = match (self.salary,other.salary) {
(Some(s1), Some(s2)) => {
// 注意:f64 的 partial_cmp 返回 Option,需要处理
s2.partial_cmp(&s1).unwrap_or(Ordering::Equal)
},
(Some(_),None) => Ordering::Less, // 有薪资 > 无薪资
(None, Some(_)) => Ordering::Greater, // 无薪资 < 有薪资
(None, None) => Ordering::Equal,
};
// 如果薪资相同,按工作经验降序排序
let exp_ordering = salary_ordering.then_with(|| {
match (self.years_of_experience,other.years_of_experience) {
(Some(e1), Some(e2)) => e2.cmp(&e1),
(Some(_),None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(None, None) => Ordering::Equal,
}
});
// 如果薪资和经验都相同,按年龄升序排序(年轻的排前面)
let age_ordering = exp_ordering.then_with(|| {
match (self.age,other.age) {
(Some(a1),Some(a2)) => a1.cmp(&a2), // 年龄小的排前面
(Some(_), None) => Ordering::Less,
(None,Some(_)) => Ordering::Equal,
(None,None) => Ordering::Equal,
}
});
// 如果所有条件都相同,按姓名排序
age_ordering.then_with(|| self.name.cmp(&other.name))
}
}
for_each()
rust
let numbers = vec![1, 2, 3, 4, 5];
// 基本用法:打印每个元素
println!("打印所有元素:");
numbers.iter().for_each(|x| println!("元素: {}", x));
// 实际应用:批量处理数据
let mut results: Vec<i32> = Vec::new();
numbers.iter()
.filter(|&&x|x % 2 == 0)
.for_each(|&x|{
let processed = x * 10;
results.push(processed);
println!("处理 {} -> {}", x, processed);
});
println!("处理结果: {:?}", results); //输出: 处理结果: [20, 40]
// 与map对比:for_each用于副作用,map用于转换
let squared: Vec<i32> = numbers.iter().map(|x| x * x).collect();
println!("平方结果: {:?}", squared);
// for_each更适合有副作用的操作
numbers.iter()
.enumerate()
.for_each(|(i,&x)|{
println!("索引 {}: 值 {}", i, x);
});
partition()
根据闭包的布尔返回值(true/false),将迭代器元素分割到两个独立的集合中。
rust
let numbers = vec![1, 2, 3, 4, 5, 6];
// 将数字分为偶数和奇数两组
let (even, odd): (Vec<i32>, Vec<i32>) = numbers.into_iter().partition(|&x| x % 2 == 0);
println!("偶数: {:?}", even); // 输出: 偶数: [2, 4, 6]
println!("奇数: {:?}", odd); // 输出: 奇数: [1, 3, 5]
// 处理 Result类型的数据
let strings = vec!["tofu", "93", "18", "abc"];
// 初步分割:成功解析和解析错误的数字
let (ok_results, err_results): (Vec<_>, Vec<_>) = strings.into_iter()
.map(|s| s.parse::<i32>()).partition(Result::is_ok);
// 提取实际值(生产环境应处理Option)
let numbers: Vec<_> = ok_results.into_iter().map(Result::unwrap).collect();
let errors: Vec<_> = err_results.into_iter().map(Result::unwrap_err).collect();
println!("成功解析的数字: {:?}", numbers); // 输出: 成功解析的数字: [93, 18]
println!("解析错误的数字: {:?}", errors); // 输出: 解析错误的数字: [ParseIntError { kind: InvalidDigit }, ParseIntError { kind: InvalidDigit }]
unzip()
将每个元素都是元组的迭代器(如 (A, B))分离成两个独立的集合,一个包含所有元组的第一个元素,另一个包含第二个元素。
rust
let pairs = vec![("Alice", 30), ("Bob", 25), ("Charlie", 35)];
// 将(name, age)元组解构成两个Vec
let (names, ages): (Vec<&str>, Vec<i32>) = pairs.into_iter().unzip();
println!("姓名: {:?}", names); // 输出: 姓名: ["Alice", "Bob", "Charlie"]
println!("年龄: {:?}", ages); // 输出: 年龄: [30, 25, 35]
// 处理 Vec的迭代器(结合 enumerate()使用)
let fruits = vec!["apple", "banana", "cherry"];
// 结合enumerate(),获取(索引, 值)然后解构
let (indices, values): (Vec<usize>, Vec<&str>) = fruits.iter().enumerate().unzip();
println!("索引: {:?}", indices); // 输出: 索引: [0, 1, 2]
println!("值: {:?}", values); // 输出: 值: ["apple", "banana", "cherry"]
3. 迭代器适配器
迭代器适配器返回新的迭代器,可以链式调用,这些操作是惰性的 。
map()
对迭代器的每个元素应用一个闭包进行转换,生成一个新元素的迭代器。
rust
let numbers = vec![1, 2, 3, 4, 5];
// 将每个数字乘以2
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
println!("翻倍后的数字: {:?}", doubled); // 输出: 翻倍后的数字: [2, 4, 6, 8, 10]
// 将数字转换为字符串
let strings: Vec<String> = numbers.iter().map(|x| format!("数字: {}", x)).collect();
println!("字符串表示: {:?}", strings); // 输出: 字符串表示: ["数字: 1", "数字: 2", "数字: 3", "数字: 4", "数字: 5"]
// 处理 Option 类型,对 Option<T> 中的值进行转换,如果存在的话,Some(x) → Some(f(x)),None → None,避免显式的 match 语句,使代码更简洁
// 处理存在的值
let number = Some(5);
let doubled = number.map(|x| x * 2);
println!("处理存在的值: {:?}", doubled); // 输出: 处理存在的值: Some(10)
// 处理不存在的值
let empty: Option<i32> = None;
let result = empty.map(|x| x * 2);
println!("处理不存在的值: {:?}", result); // 输出: 处理不存在的值: None
// 链式处理字符串
let text = Some(" Hello World ".to_string());
let processed = text
.map(|s| s.trim().to_string()) // 去除空格
.map(|s| s.to_uppercase()) // 转大写
.map(|s| format!("Processed: {}", s)); // 添加前缀
println!("{:?}", processed); // 输出: Some("Processed: HELLO WORLD")
// 处理 None 的情况
let no_text: Option<String> = None;
let result = no_text.map(|s| s.trim().to_string()).map(|s| s.to_uppercase());
println!("{:?}", result); // 输出: None
与原始 match 语句对比
使用 map(简洁)
rust
let result = some_option
.map(|x| transform1(x))
.map(|x| transform2(x))
.map(|x| transform3(x));
使用 match(冗长)
rust
let result = match some_option {
Some(x) => {
let step1 = transform1(x);
match step1 {
Some(y) => {
let step2 = transform2(y);
match step2 {
Some(z) => transform3(z),
None => None,
}
},
None => None,
}
},
None => None,
};
filter()
接受一个闭包,该闭包对每个元素返回 bool 值,只保留满足条件的元素:
rust
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 过滤出偶数
let even_numbers: Vec<i32> = numbers.iter().filter(|&x| x % 2 == 0).cloned().collect();
println!("偶数: {:?}", even_numbers); // 输出: 偶数: [2, 4, 6, 8, 10]
// 过滤出大于5的数
let greater_than_five: Vec<i32> = numbers.iter().filter(|&x| *x > 5).cloned().collect();
println!("大于5的数: {:?}", greater_than_five); // 输出: 大于5的数: [6, 7, 8, 9, 10]
let words = vec!["apple", "banana", "cherry", "date", "elderberry"];
// 过滤出长度大于5的单词
let long_words: Vec<&str> = words.iter().filter(|word| word.len() > 5).cloned().collect();
println!("长单词: {:?}", long_words); // 输出: 长单词: ["banana", "cherry", "elderberry"]
// 过滤出以'a'开头 'e'结尾的单词
let a_words: Vec<&str> = words.iter().filter(|word| word.starts_with('a') && word.ends_with('e')).cloned().collect();
println!("以'a'开头 'e'结尾的单词: {:?}", a_words); // 输出: 以'a'开头 'e'结尾的单词: ["apple"]
// 过滤出 Some 值
let mixed_values = vec![Some(1), None, Some(3), None, Some(5)];
let some_values: Vec<i32> = mixed_values.iter().filter(|x| x.is_some()).map(|x| x.unwrap()).collect();
println!("Some值: {:?}", some_values); // 输出: Some值: [1, 3, 5]
// 更简洁的方式
let some_values2: Vec<i32> = mixed_values.iter().flatten().cloned().collect();
println!("Some值(简洁版): {:?}", some_values2); // 输出: Some值(简洁版): [1, 3, 5]
filter_map()
结合了 filter() 和 map() 的功能,同时进行过滤和转换:
rust
let strings = vec!["1", "2", "abc", "4", "def", "6"];
// 尝试将字符串解析为数字,只保留成功解析的
let numbers: Vec<i32> = strings.iter().filter_map(|s| s.parse::<i32>().ok()).collect();
println!("解析成功的数字: {:?}", numbers); // 输出: 解析成功的数字: [1, 2, 4, 6]
let mixed_values = vec![Some(1), None, Some(3), None, Some(5)];
// 提取所有 Some 值
let values: Vec<i32> = mixed_values.iter().filter_map(|&x| x).collect();
println!("提取的值: {:?}", values); // 输出: 提取的值: [1, 3, 5]
// 实际应用场景
let raw_data = vec!["10", "-5", "abc", "20", "", "30", "xyz"];
// 清洗数据: 解析为正整数
let clean_data: Vec<u32> = raw_data.iter().filter_map(|s| {
s.parse::<i32>().ok()
.filter(|&n| n > 0)
.map(|n| n as u32)
}).collect();
println!("清洗后的数据: {:?}", clean_data); // 输出: 清洗后的数据: [10, 20, 30]
// 提取文件扩展名
let file_paths = vec![
"document.txt",
"folder/",
"image.jpg",
"",
"script.rs",
"backup.bak",
];
let extensions: Vec<&str> = file_paths.iter()
.filter_map(|path| {
if path.is_empty() || path.ends_with('/') {
None
} else {
path.rfind('.').map(|pos| &path[pos + 1..])
}
}).collect();
println!("文件扩展名: {:?}", extensions); // 输出: 文件扩展名: ["txt", "jpg", "rs", "bak"]
与单独使用 filter 和 map 的对比
使用 filter_map
rust
let result: Vec<i32> = strings.iter()
.filter_map(|s| s.parse::<i32>().ok())
.collect();
分别使用 filter 和 map(不推荐)
rust
let result: Vec<i32> = strings.iter()
.filter(|s| s.parse::<i32>().is_ok()) // 先过滤
.map(|s| s.parse::<i32>().unwrap()) // 再转换(重复解析!)
.collect();
注意事项:
-
闭包必须返回 Option 类型
-
None 值会被自动过滤掉
-
Some 值会被展开并包含在结果中
-
适用于"可能成功"的转换操作
enumerate()
为迭代器中的每个元素添加从0开始的递增索引,enumerate() 将原本的迭代器转换为返回 (索引, 元素) 元组的迭代器:
rust
let fruits = vec!["apple", "banana", "cherry"];
// 使用 enumerate 获取索引和值
for (index, fruit) in fruits.iter().enumerate() {
println!("索引 {}: {}", index, fruit);
}
// 输出:
// 索引 0: apple
// 索引 1: banana
// 索引 2: cherry
// 条件过滤(基于索引)
let data = vec!["a", "b", "c", "d", "e", "f"];
// 获取奇数位置的元素
let odd_positions: Vec<&str> = data.iter()
.enumerate()
.filter(|(index, _)| index % 2 == 1)
.map(|(_, value)| *value)
.collect();
println!("奇数位置元素: {:?}", odd_positions); // 输出: 奇数位置元素: ["b", "d", "f"]
let items = vec!["first", "second", "third"];
//创建值到索引的映射
let value_to_index: std::collections::HashMap<&str, usize> = items.iter()
.enumerate()
.map(|(index, &value)| (value, index))
.collect();
println!("{:?}", value_to_index); // 输出: {"third": 2, "second": 1, "first": 0}
// 批处理数据
let large_dataset = (1..=100).collect::<Vec<i32>>();
let batch_size = 10;
// 按批处理并显示进度
for (batch_index, batch) in large_dataset.chunks(batch_size).enumerate() {
println!("处理第 {} 批,包含 {} 个元素", batch_index + 1, batch.len());
// 实际处理逻辑...
}
take() 和 skip()
take() 方法 : 限制迭代器最多返回前 n 个元素
skip() 方法 : 跳过迭代器的前 n 个元素
rust
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 使用 take() 获取前几个元素
let first_three: Vec<i32> = numbers.iter().take(3).copied().collect();
println!("前三个元素: {:?}", first_three);
// 输出: 前三个元素: [1, 2, 3]
// 使用 skip() 跳过前几个元素
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let after_third: Vec<i32> = numbers.iter().skip(3).copied().collect();
println!("跳过前三个后的元素: {:?}", after_third);
// 输出: 跳过前三个后的元素: [4, 5, 6, 7, 8, 9, 10]
// 实现分页效果:跳过前几项并获取指定数量
let data = (1..=100).collect::<Vec<i32>>();
let page_size = 10;
let page_number = 3;
let page_data: Vec<i32> = data.iter()
.skip((page_number - 1) * page_size) // 跳过前面的页面
.take(page_size) // 只取当前页面的数据
.copied()
.collect();
println!("第{}页数据: {:?}", page_number, page_data);
// 输出: 第3页数据: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
// 对无限迭代器使用 take()
let infinite = (0..).map(|x| x * 2);
let finite: Vec<i32> = infinite
.take(5)
.collect();
println!("无限序列的前5个偶数: {:?}", finite);
// 输出: 无限序列的前5个偶数: [0, 2, 4, 6, 8]
// 在无限迭代器中跳过一些元素
let fibonacci = std::iter::successors(Some((0, 1)), |&(a, b)| Some((b, a + b)))
.map(|(a, _)| a);
let fib_after_5: Vec<u32> = fibonacci
.skip(5)
.take(5)
.collect();
println!("斐波那契数列第6到第10项: {:?}", fib_after_5);
// 输出: 斐波那契数列第6到第10项: [5, 8, 13, 21, 34]
//边界情况处理
// take() 超出范围的情况
let short_vec = vec![1, 2, 3];
let taken: Vec<i32> = short_vec.iter()
.take(10) // 请求10个元素,但只有3个
.copied()
.collect();
println!("take(10) 从3个元素中获取: {:?}", taken);
// 输出: take(10) 从3个元素中获取: [1, 2, 3]
// skip() 超出范围的情况
let short_vec = vec![1, 2, 3];
let skipped: Vec<i32> = short_vec.iter()
.skip(10) // 跳过10个元素,但只有3个
.copied()
.collect();
println!("skip(10) 从3个元素中跳过: {:?}", skipped);
// 输出: skip(10) 从3个元素中跳过: []
take_while() 和 skip_while()
take_while() 方法 : 从迭代器中获取元素,直到闭包返回 false 为止。
skip_while() 方法 : 跳过迭代器中满足条件的前缀元素。
一旦条件第一次失败,两者都立即停手 ,后面无论再有满足条件的元素都不再理会。
因此它们只对有序(或至少把"相关数据"放前面)的数据才有"业务意义"。
rust
// take_while() 基础用法
// 获取小于5的所有元素
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let less_than_five: Vec<i32> = numbers.iter()
.take_while(|&&x| x < 5)
.copied()
.collect();
println!("小于5的元素: {:?}", less_than_five);
// 输出: 小于5的元素: [1, 2, 3, 4]
// 处理有序数据直到遇到特定条件
let temperatures = vec![18, 20, 22, 25, 32, 28, 26];
let valid_sequence: Vec<i32> = temperatures.iter()
.take_while(|&&temp| temp <= 30)
.copied()
.collect();
println!("遇到高温前的温度序列: {:?}", valid_sequence);
// 输出: 遇到高温前的温度序列: [18, 20, 22, 25]
// skip_while() 基础用法
// 跳过所有小于5的元素
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let after_less_than_five: Vec<i32> = numbers.iter()
.skip_while(|&&x| x < 5)
.copied()
.collect();
println!("跳过小于5的元素后: {:?}", after_less_than_five);
// 输出: 跳过小于5的元素后: [5, 6, 7, 8, 9, 10]
// 跳过负数前缀
let data = vec![-3, -1, 0, 2, -5, 7, 8];
let positive_start: Vec<i32> = data.iter()
.skip_while(|&&x| x < 0)
.copied()
.collect();
println!("跳过负数前缀后: {:?}", positive_start);
// 输出: 跳过负数前缀后: [0, 2, -5, 7, 8]
zip()
将两个迭代器合并为元组迭代器。
rust
// 基本的数字配对
let numbers1 = vec![1, 2, 3, 4];
let numbers2 = vec![10, 20, 30, 40, 50]; // 注意这里多一个元素
let paired: Vec<(i32, i32)> = numbers1.iter()
.zip(numbers2.iter())
.map(|(&a, &b)| (a, b))
.collect();
println!("配对结果: {:?}", paired);
// 输出: 配对结果: [(1, 10), (2, 20), (3, 30), (4, 40)]
// 注意:只配对了4个元素,因为第一个迭代器较短
// 实际应用场景
// 将名称和分数配对
let names = vec!["Alice", "Bob", "Charlie", "David"];
let scores = vec![95, 87, 92, 78, 85]; // 多一个分数
let student_scores: Vec<(&str, i32)> = names.iter()
.zip(scores.iter())
.map(|(&name, &score)| (name, score))
.collect();
println!("学生分数配对: {:?}", student_scores);
// 输出: 学生分数配对: [("Alice", 95), ("Bob", 87), ("Charlie", 92), ("David", 78)]
// 计算相邻元素的差值
let data = vec![10, 15, 25, 30, 45];
let differences: Vec<i32> = data.iter()
.zip(data.iter().skip(1))
.map(|(a, b)| b - a)
.collect();
println!("相邻元素差值: {:?}", differences);
// 输出: 相邻元素差值: [5, 10, 5, 15]
// 复杂数据处理
// 处理坐标点
let x_coords = vec![1.0, 2.0, 3.0, 4.0];
let y_coords = vec![1.5, 2.5, 3.5];
let points: Vec<(f64, f64)> = x_coords.iter()
.zip(y_coords.iter())
.map(|(&x, &y)| (x, y))
.collect();
println!("坐标点: {:?}", points);
// 输出: 坐标点: [(1.0, 1.5), (2.0, 2.5), (3.0, 3.5)]
// 计算点之间的距离
let distances: Vec<f64> = points.iter()
.zip(points.iter().skip(1))
.map(|(&(x1, y1), &(x2, y2))| {
((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt()
})
.collect();
println!("相邻点间距离: {:?}", distances);
// 输出: 相邻点间距离: [1.4142135623730951, 1.4142135623730951]
// 嵌套使用示例
// 三元组配对(通过嵌套zip实现)
let a = vec![1, 2, 3];
let b = vec![10, 20, 30];
let c = vec![100, 200, 300];
let triplets: Vec<(i32, i32, i32)> = a.iter()
.zip(b.iter().zip(c.iter()))
.map(|(&x, (&y, &z))| (x, y, z))
.collect();
println!("三元组: {:?}", triplets);
// 输出: 三元组: [(1, 10, 100), (2, 20, 200), (3, 30, 300)]
chain()
将两个迭代器连接成一个连续的迭代器。
rust
// 连接两个向量 不同长度连接同理 空迭代器连接会只保留不空迭代器的元素
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
let combined: Vec<i32> = vec1.iter()
.chain(vec2.iter())
.copied()
.collect();
println!("连接结果: {:?}", combined);
// 输出: 连接结果: [1, 2, 3, 4, 5, 6]
// 连接范围和向量
let range_data = 1..=3;
let vec_data = vec![4, 5, 6];
let combined: Vec<i32> = range_data
.chain(vec_data.iter().copied())
.collect();
println!("范围与向量连接: {:?}", combined);
// 输出: 范围与向量连接: [1, 2, 3, 4, 5, 6]
// 连接多个迭代器
let part1 = vec![1, 2];
let part2 = vec![3, 4];
let part3 = vec![5, 6];
let multiple_chained: Vec<i32> = part1.iter()
.chain(part2.iter())
.chain(part3.iter())
.copied()
.collect();
println!("多个迭代器连接: {:?}", multiple_chained);
// 输出: 多个迭代器连接: [1, 2, 3, 4, 5, 6]
// 处理可选数据
let required_data = vec!["name", "age"];
let optional_data: Option<Vec<&str>> = Some(vec!["email", "phone"]);
let all_fields: Vec<&str> = required_data.iter()
.chain(optional_data.iter().flat_map(|v| v.iter()))
.copied()
.collect();
println!("所有字段: {:?}", all_fields);
// 输出: 所有字段: ["name", "age", "email", "phone"]
// 实际应用场景
// 处理文件路径组件
let base_path = vec!["home", "user"];
let sub_path = vec!["documents", "projects", "rust"];
let full_path: Vec<&str> = base_path.iter()
.chain(sub_path.iter())
.copied()
.collect();
println!("完整路径组件: {:?}", full_path);
// 输出: 完整路径组件: ["home", "user", "documents", "projects", "rust"]
// 合并不同来源的数据
let priority_items = vec!["urgent_task1", "urgent_task2"];
let regular_items = vec!["task1", "task2", "task3"];
let all_tasks: Vec<&str> = priority_items.iter()
.chain(regular_items.iter())
.copied()
.collect();
println!("所有任务(优先级优先): {:?}", all_tasks);
// 输出: 所有任务(优先级优先): ["urgent_task1", "urgent_task2", "task1", "task2", "task3"]
flat_map()
对迭代器中的每个元素应用函数,该函数返回一个迭代器,然后将所有结果迭代器展平成一个单一迭代器。
rust
// 展平嵌套向量
let nested_vecs = vec![vec![1, 2], vec![3, 4, 5], vec![6]];
let flattened: Vec<i32> = nested_vecs.iter()
.flat_map(|v| v.iter())
.copied()
.collect();
println!("展平结果: {:?}", flattened);
// 输出: 展平结果: [1, 2, 3, 4, 5, 6]
// 将字符串分割成字符并展平
let words = vec!["hello", "world", "rust"];
let all_chars: Vec<char> = words.iter()
.flat_map(|word| word.chars())
.collect();
println!("所有字符: {:?}", all_chars);
// 输出: 所有字符: ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'r', 'u', 's', 't']
// 处理句子中的单词
let sentences = vec!["hello world", "rust is great"];
let all_words: Vec<&str> = sentences.iter()
.flat_map(|sentence| sentence.split_whitespace())
.collect();
println!("所有单词: {:?}", all_words);
// 输出: 所有单词: ["hello", "world", "rust", "is", "great"]
// 展平 Option 值
let numbers = vec![1, 2, 3, 4, 5];
let even_numbers_doubled: Vec<i32> = numbers.iter()
.flat_map(|&n| {
if n % 2 == 0 {
Some(n * 2)
} else {
None
}
})
.collect();
println!("偶数翻倍结果: {:?}", even_numbers_doubled);
// 输出: 偶数翻倍结果: [4, 8]
// 处理可能失败的转换
let strings = vec!["1", "2", "abc", "4", "5.5"];
let parsed_integers: Vec<i32> = strings.iter()
.flat_map(|s| s.parse::<i32>().ok())
.collect();
println!("成功解析的整数: {:?}", parsed_integers);
// 输出: 成功解析的整数: [1, 2, 4]
复杂数据结构处理
rust
let departments = vec![
Department {
name: "Engineering".to_string(),
employees: vec!["Alice".to_string(), "Bob".to_string()],
},
Department {
name: "Marketing".to_string(),
employees: vec!["Charlie".to_string(), "David".to_string(), "Eve".to_string()],
},
];
let all_employees: Vec<String> = departments.iter()
.flat_map(|dept| dept.employees.iter().cloned())
.collect();
println!("所有员工: {:?}", all_employees);
// 输出: 所有员工: ["Alice", "Bob", "Charlie", "David", "Eve"]
// 生成范围序列
let numbers = vec![1, 2, 3,4];
let range_sequences: Vec<i32> = numbers.iter()
.flat_map(|&n| 1..=n)
.collect();
println!("范围序列: {:?}", range_sequences);
// 输出: 范围序列: [1, 1, 2, 1, 2, 3, 1, 2, 3, 4]
// 处理多维数据
let matrix = vec![
vec![1, 2, 3],
vec![4, 5],
vec![6, 7, 8, 9]
];
let sum_of_all_elements: i32 = matrix.iter()
.flat_map(|row| row.iter())
.sum();
println!("所有元素的和: {}", sum_of_all_elements);
// 输出: 所有元素的和: 45
flatten()
将嵌套的迭代器结构扁平化为单一层次的迭代器。
java
// 扁平化嵌套Vec
let nested_vecs = vec![vec![1, 2, 3], vec![4, 5], vec![6, 7, 8, 9]];
let flattened: Vec<i32> = nested_vecs.into_iter().flatten().collect();
println!("Flattened vector: {:?}", flattened);
// 输出: Flattened vector: [1, 2, 3, 4, 5, 6, 7, 8, 9]
// 处理Option嵌套
let nested_options = vec![Some(Some(1)), Some(None), None, Some(Some(2))];
let flattened: Vec<Option<i32>> = nested_options.into_iter().flatten().collect();
println!("Flattened options: {:?}", flattened);
// 输出: Flattened options: [Some(1), None, Some(2)]
// 扁平化字符串向量
let words = vec![vec!["hello", "world"], vec!["rust", "is", "awesome"], vec!["flatten", "demo"]];
// 连接成一个字符串
let sentence: String = words.into_iter().flatten().collect::<Vec<_>>().join(" ");
println!("Sentence: {}", sentence);
// 输出: Sentence: hello world rust is awesome flatten demo
// 处理迭代器的迭代器
let ranges = vec![0..3, 5..8, 10..12];
// 使用 flatten 展平这些范围 然后链式调用其他适配器
let even_numbers: Vec<i32> = ranges.into_iter()
.flatten()
.filter(|&x| x % 2 == 0)
.collect();
println!("Even numbers from flattened ranges: {:?}", even_numbers);
// 输出: Even numbers from flattened ranges: [0, 2, 6, 10]
flatten() 与 flat_map() 对比
| 特性 | flatten() |
flat_map() |
|---|---|---|
| 功能 | 仅扁平化已有的嵌套迭代器 | 映射后扁平化,相当于map() +flatten() |
| 参数 | 无参数 | 接受一个闭包函数FnMut(Self::Item) -> U |
| 使用场景 | 处理现成的嵌套结构如Vec<Vec<T>> |
需要先转换再扁平化的场景 |
| 代码示例 | vec![vec![1,2], vec![3,4]].into_iter().flatten() |
`vec![1,2].into_iter().flat_map( |
| 等价写法 | - | iter.flat_map(f) ≈iter.map(f).flatten() |
| 返回类型 | Flatten<Self> |
FlatMap<Self, U, F> |
| 灵活性 | 专门化,仅处理嵌套结构 | 更灵活,可自定义映射逻辑 |
cycle()
创建一个无限重复原迭代器序列的迭代器,产生的迭代器是无限的,必须配合 take()、take_while() 等适配器使用来限制长度
rust
// 基本使用 - 重复简单序列
let numbers = vec![1, 2, 3];
let cycled: Vec<i32> = numbers.iter().cycle().take(10).copied().collect();
println!("Cycled numbers: {:?}", cycled);
// 输出: Cycled numbers: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
// 创建无限序列并使用条件限制
let numbers = vec![1, 2, 3, 4];
let limited_cycle: Vec<i32> = numbers.iter()
.cycle()
.take_while(|&&x| x <= 3) // 这个例子中只会取到第一个1,2,3
.copied()
.collect();
println!("Limited cycle: {:?}", limited_cycle);
// 输出: Limited cycle: [1, 2, 3]
inspect()
inspect() 不改变迭代器的元素,只是在迭代过程中执行指定操作
rust
// 链式操作中的调试
let words = vec!["hello", "world", "rust", "programming"];
let processed: Vec<String> = words.iter()
.inspect(|word| println!("Original word: {}", word))
.map(|word| word.to_uppercase())
.inspect(|word| println!("Uppercase: {}", word))
.filter(|word| word.len() > 4)
.inspect(|word| println!("Long word: {}", word))
.collect();
println!("Filtered long words: {:?}", processed);
// 输出:
// Original word: hello
// Uppercase: HELLO
// Long word: HELLO
// Original word: world
// Uppercase: WORLD
// Long word: WORLD
// Original word: rust
// Uppercase: RUST
// Original word: programming
// Uppercase: PROGRAMMING
// Long word: PROGRAMMING
// Filtered long words: ["HELLO", "WORLD", "PROGRAMMING"]
// 复杂数据处理调试
let numbers = vec![1, 2, 3, 4, 5, 6];
let result: Vec<i32> = numbers.iter()
.inspect(|&n| println!("Starting with: {}", n))
.map(|&n| n * n)
.inspect(|&n| println!("Squared: {}", n))
.filter(|&n| n % 2 == 0)
.inspect(|&n| println!("Even square: {}", n))
.take(3)
.inspect(|&n| println!("Taking: {}", n))
.collect();
println!("Final result: {:?}", result);
// 输出:
// Starting with: 1
// Squared: 1
// Starting with: 2
// Squared: 4
// Even square: 4
// Taking: 4
// Starting with: 3
// Squared: 9
// Starting with: 4
// Squared: 16
// Even square: 16
// Taking: 16
// Starting with: 5
// Squared: 25
// Starting with: 6
// Squared: 36
// Even square: 36
// Taking: 36
// Final result: [4, 16, 36]
step_by()
让迭代器按照指定的步长跳过元素。第一个元素总是会被访问,然后每隔step-1个元素访问一次。
rust
let numbers = 0..=10;
// 步长为2:访问偶数位置元素
for i in numbers.clone().step_by(2) {
print!("{} ", i);
};
// 输出: 0 2 4 6 8 10
// 步长为3
for i in numbers.clone().step_by(3) {
print!("{} ", i);
}
// 输出: 0 3 6 9
// 步长为1相当于正常迭代
for i in numbers.clone().step_by(1) {
print!("{} ", i);
}
// 输出: 0 1 2 3 4 5 6 7 8 9 10
rev()
反转迭代器顺序:
rust
let numbers = vec!["早餐", "午餐", "晚餐"];
println!("正常顺序:");
for meal in numbers.iter() {
print!("{} ", meal);
}
println!();
println!("反向顺序:");
for meal in numbers.iter().rev() {
print!("{} ", meal);
}
//输出:
// 正常顺序:
// 早餐 午餐 晚餐
// 反向顺序:
// 晚餐 午餐 早餐
4. 高级迭代器技巧
自定义迭代器
通过实现 Iteratortrait 创建自定义迭代器 (实现一个简单的计数器) :
rust
#[derive(Debug, Clone)]
struct Counter {
current: u32,
max: u32,
step: u32,
}
impl Counter {
fn new(max:u32) -> Self{
Counter{
current: 0,
max: max,
step: 1,
}
}
// 允许自定义起始点
fn from_start(start:u32,max:u32) -> Self{
Counter {
current: start.saturating_sub(1),
max,
step: 1,
}
}
// 允许自定义步长
fn with_step(self,step:u32) -> Self{
Counter { step, ..self }
}
// 重置计数器
fn reset(&mut self){
self.current = 0;
}
// 查看当前值而不消费它 (窥视)
fn peek(&self) -> Option<u32>{
if self.current < self.max {
Some(self.current + 1) // 注意:因为next()返回的是current+1后的值
} else {
None
}
}
}
impl Iterator for Counter{
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.max {
// 防止溢出,并使用步长
self.current = self.current.saturating_add(self.step);
if self.current <= self.max {
Some(self.current)
}else {
None
}
} else {
None
}
}
// 提供大小提示,有助于优化(例如collect预分配内存)
fn size_hint(&self) -> (usize, Option<usize>) {
if self.current >= self.max {
(0, Some(0))
}else {
let remaining = (self.max - self.current) as usize;
// 考虑步长后更精确的大小计算
let exact = remaining / self.step as usize + if remaining % self.step as usize > 0 { 1 } else { 0 };
(exact, Some(exact))
}
}
}
fn main() {
// 从5开始计数到10
let counter1 = Counter::from_start(5, 10);
println!("从5开始: {:?}", counter1.clone().collect::<Vec<_>>());
// 输出: 从5开始: [5, 6, 7, 8, 9, 10]
// 实用步长为2
let counter2 = Counter::new(10).with_step(2);
println!("步长为2: {:?}", counter2.collect::<Vec<_>>());
// 输出: 步长为2: [2, 4, 6, 8, 10]
// 使用peek方法
let mut counter3 = Counter::new(3);
println!("第一次next前peek: {:?}", counter3.peek()); // Some(1)
println!("第一次next: {:?}", counter3.next()); // Some(1)
println!("第二次next后peek: {:?}", counter3.peek()); // Some(2)
}
模拟真实应用场景 (实现文件分页读取器)
rust
use std::fs::File;
use std::io::{self, BufRead, BufReader, Seek, SeekFrom};
use std::path::Path;
/// 文件分页读取器
/// 用于按行分页读取大文件,避免一次性加载整个文件到内存中。
struct FilePager {
reader: BufReader<File>,
page_size: usize,
current_line: usize,
total_lines: Option<usize>, // 总行数,初始为None,惰性计算
}
type Page = (usize, Vec<String>);
impl FilePager {
/// 尝试创建一个新的 FilePager。
/// 如果文件无法打开,则返回 io::Error。
pub fn try_new<P:AsRef<Path>>(file_path:P,page_size:usize) -> io::Result<Self>{
let file = File::open(file_path)?;
let reader = BufReader::new(file);
Ok(FilePager {
reader,
page_size,
current_line: 0,
total_lines: None, // 先不计算总行数
})
}
/// 计算文件的总行数(惰性计算并缓存)。
/// 这是一个相对昂贵的操作,因为它需要读取整个文件。
fn calculate_total_lines(&mut self) -> io::Result<usize> {
if let Some(total) = self.total_lines {
return Ok(total);
};
// 保存当前文件读取位置
let current_pos = self.reader.stream_position()?;
// 重置到文件开头
self.reader.seek(SeekFrom::Start(0))?;
let mut count = 0;
let mut buffer = String::new();
while self.reader.read_line(&mut buffer)? > 0 {
count += 1;
buffer.clear();
};
// 恢复之前的读取位置
self.reader.seek(SeekFrom::Start(current_pos))?;
self.total_lines = Some(count);
Ok(count)
}
/// 获取总页数。
pub fn total_pages(&mut self) -> io::Result<usize>{
let total_lines = self.calculate_total_lines()?;
if total_lines == 0 {
Ok(0)
}else {
Ok((total_lines + self.page_size - 1) / self.page_size)
}
}
}
impl Iterator for FilePager {
type Item = io::Result<Page>;
fn next(&mut self) -> Option<Self::Item> {
// 先检查总行数,如果为0则直接返回None
let total_lines = match self.calculate_total_lines() {
Ok(n) => n,
Err(e) => return Some(Err(e)), // 计算行数时出错
};
if self.current_line >= total_lines {
return None;
}
let mut page_lines: Vec<String> = Vec::with_capacity(self.page_size);
let page_number = (self.current_line / self.page_size) + 1;
let mut lines_read = 0;
// 读取一页的数据
while lines_read < self.page_size {
let mut line = String::new();
match self.reader.read_line(&mut line) {
Ok(0) => break,
Ok(_) => {
// 移除行尾的换行符
if line.ends_with('\n') {
line.pop();
if line.ends_with('\r') {
line.pop();
}
}
page_lines.push(line);
self.current_line += 1;
lines_read += 1;
}
Err(e) => {
// 如果某一行读取失败,返回错误
return Some(Err(e));
}
}
}
Some(Ok((page_number, page_lines)))
}
}
fn main() -> io::Result<()> {
// 创建一个示例文件
let sample_content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10";
let file_path = "sample.txt";
std::fs::write(file_path, sample_content)?;
println!("=== 文件分页读取器示例 ===");
// 创建分页读取器,每页3行
let mut file_pager = FilePager::try_new(file_path, 6)?;
// 先打印总页数信息
if let Ok(total_pages) = file_pager.total_pages() {
println!("文件总页数: {}", total_pages);
}
// 使用 for 循环逐页处理(自动处理迭代器和 Result)
println!("\n正向分页读取:");
for page_result in &mut file_pager {
match page_result {
Ok((page_num, lines)) => {
println!("第 {} 页 (共 {} 行): {:?}", page_num, lines.len(), lines);
}
Err(e) => eprintln!("读取页面时发生错误: {}", e),
}
}
// 清理示例文件
std::fs::remove_file(file_path)?;
Ok(())
}
5. 性能优化与最佳实践
惰性求值优势
迭代器适配器是惰性的,只有在调用消费适配器时才会执行计算,这使得 Rust 编译器能够进行大量优化 。
优先选择迭代器而非循环
对于集合操作,优先使用迭代器方法而非手写循环,因为:
- 更安全:避免索引越界等错误
- 更高效:编译器能进行更好的优化
- 更简洁:代码更易读易维护
合理安排适配器顺序
rust
// 模拟耗时的检查函数
fn expensive_check(x: &i32) -> bool {
// 模拟复杂计算或IO操作
x % 2 == 0 && x > &10
}
// 模拟耗时的转换函数
fn expensive_transform(x: &i32) -> i32 {
// 模拟复杂计算
x * x + 2 * x + 1
}
fn main() {
let large_vector: Vec<i32> = (1..10000).collect();
// 推荐:先过滤再转换,减少不必要的计算
let efficient: Vec<i32> = large_vector.iter()
.filter(|x| expensive_check(x)) // 先过滤掉不需要的元素
.map(|x| expensive_transform(x)) // 只对剩余元素进行转换
.collect();
// 不推荐:先转换再过滤,可能做无用功
let inefficient: Vec<i32> = large_vector.iter()
.map(|x| expensive_transform(x)) // 对所有元素进行转换
.filter(|x| expensive_check(x)) // 然后过滤掉大部分
.collect();
}