Rust 常用语法速记 - 迭代器

Rust 常用语法速记 - 迭代器

Rust 的迭代器(Iterator)是一种强大而高效的数据处理工具 ,它允许以声明式的方式遍历和操作集合中的元素。迭代器是 Rust 零成本抽象的典范,意味着它在提供高级抽象的同时几乎不产生运行时开销。

1. 迭代器基础概念

什么是迭代器?

迭代器是任何实现了 Iterator​trait 的类型,它能够逐个产生序列中的值。迭代器的核心特点是惰性求值(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]

何时需要使用该语法

  1. 编译器无法推断类型时
  2. 代码清晰性:明确指定集合类型增强可读性
  3. 复杂链式操作:中间步骤类型信息可能丢失时

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();
}

相关推荐
清空mega2 小时前
从零开始搭建 flask 博客实验(4)
后端·python·flask
bcbnb2 小时前
iPhone HTTPS 抓包,从无法抓包到定位问题的流程(Charles/tcpdump/Wireshark/Sniffmaster)
后端
Data_Adventure3 小时前
TypeScript 开发者转向 Java:学习重点与思维迁移指南
后端
吴祖贤3 小时前
Spring AI 零基础入门:从踩坑到上手的完整指南
后端
code_std3 小时前
SpringBoot 登录验证码
java·spring boot·后端
Mos_x3 小时前
@RestController注解
java·后端
bcbnb3 小时前
Fiddler抓包工具使用教程,HTTPHTTPS抓包、代理配置与调试技巧全解析(附实战经验)
后端
虎子_layor4 小时前
PostgreSQL这么多优势,为什么还要使用MySQL
后端·sql