Rust中的for循环与迭代器
for 循环是迭代器的语法糖,而迭代器是 Rust 处理集合(如 Vec、HashMap)的核心抽象。
for循环
for 循环本质是自动遍历一个“可迭代对象”(实现 IntoIterator trait 的类型),底层逻辑如下:
- 对目标对象调用
into_iter()方法,将其转换为迭代器(Iterator); - 循环调用迭代器的
next()方法,每次获取一个元素(Some(T)); - 当
next()返回None时,循环终止;
for item in collection {// ...
}// 会转换为以下近似代码
{let mut iter = IntoIterator::into_iter(collection);while let Some(item) = iter.next() {// ...}
}
用法
任何实现 IntoIterator 的类型,都能直接用 for 循环遍历。常有以下使用场景:
- 遍历集合:处理
Vec、数组([T; N])、切片(&[T])等连续存储的集合;需注意所有权与借用模式,三种核心模式;- 所有权转移遍历:
into_iter();转移所有权,T类型; - 不可变借用遍历:
iter();不可变引用&T,需通过解引用(*)访问元素值。 - 可变借用遍历:
iter_mut();可变引用&mut T,需通过解引用(*)修改元素。
- 所有权转移遍历:
- 计数循环:Range区间遍历
- 左闭右开区间
(a..b) - 左闭右闭区间
(a..=b) - 反向计数(
rev()反向迭代):for i in (1..=5).rev()
- 左闭右开区间
- 键值对遍历(HashMap/BTreeMap):返回key与value的引用;
for (key, value) in &map - 遍历索引与值:
enumerate()适配器(返回(index, value)元组);for (idx, fruit) in fruits.iter().enumerate()
| 写法 | 底层机制 | 说明 |
|---|---|---|
for x in v | v.into_iter() | T(原值);所有权转移(消耗集合) |
for x in &v | v.iter() | &T(不可变引用);不可变借用(只读集合) |
for x in &mut v | v.iter_mut() | &mut T(可变引用);可变借用(修改集合) |
for i in 0..n | Range迭代器 | 值拷贝 |
使用 'label + break 'label | 控制流 | 嵌套退出 |
绑定解构
迭代器遍历与后续适配器中元素的类型,是由trait中的Item确定的;对于引用类型,可通过&(不是取地址)进行解构。以iter()(Item 是 &T)遍历为例:
- 用
x:绑定&T(保留引用),需通过*x解引用操作值; - 用
&x:解构一层引用,直接绑定T(原值),无需解引用。
fn type_of<T>(_: &T) -> &'static str {type_name::<T>()
}pub fn for_test() {let mut map = HashMap::new();map.insert("english", 80);map.insert("math", 90);// 用&解构过一次,输出:&str: i32for (&key, &value) in &map {println!("{}: {}", type_of(&key), type_of(&value));break;}// 输出:&&str: &i32for (key, value) in &map {println!("{}: {}", type_of(&key), type_of(&value));break;}
}
迭代器
Rust 的迭代器是实现了 Iterator trait 的对象;其不仅仅是一个“遍历工具”,还能惰性处理数据流,即惰性求值(lazy evaluation);多数方法不会立即执行,只是返回一个新的迭代器(直到 .collect() 或 .for_each() 等最终消费执行时,才计算)。
pub fn it_test() {// 1. 拆分单词为字符 → 2. 筛选小写字母 → 3. 转换为大写 → 4. 收集为 Vec<char>let words = vec!["Hello", "World", "Rust"];let uppercase_chars: Vec<char> = words.iter().flat_map(|s| s.chars()) // 扁平化:["H","e","l","l","o", ...].filter(|&c| c.is_ascii_lowercase()) // 筛选小写字母.map(|c| c.to_ascii_uppercase()) // 转换为大写.collect(); // 消耗迭代器,收集结果println!("{:?}", uppercase_chars); // 输出 ['E', 'L', 'L', 'O', 'O', 'R', 'L', 'D', 'U', 'S', 'T']let nums = vec![1, 2, 3, 4, 5];// 1. 求和(fold 折叠)let total = nums.iter().fold(1, |init, &x| init * x);println!("阶乘:{}", total); // 输出 120// 2. 查找第一个偶数let first_even = nums.iter().find(|&&x| x % 2 == 0);println!("第一个偶数:{:?}", first_even); // 输出 Some(2)// 3. 判断是否存在大于 5 的元素let has_gt5 = nums.iter().any(|&x| x > 5);println!("存在大于 5 的元素?{}", has_gt5); // 输出 false
}
适配器(Adapter)
适配器是返回新迭代器的方法,用于转换、筛选、组合原迭代器;多个适配器可组成“链式”,即通过把多个迭代器适配器组合在一起形成流水线式的数据处理流程。常见适配器:
| 适配器 | 功能 |
|---|---|
map(f) | 对元素应用 f,转换元素类型 |
filter(f) | 保留满足条件 f的元素,返回 bool |
flat_map(f) | 对元素应用 f,扁平化结果 |
rev() | 反向遍历(需迭代器实现 DoubleEndedIterator) |
skip(n) | 跳过前 n 个元素 |
take(n) | 取前 n 个元素,之后终止迭代 |
chain(iter2) | 拼接两个迭代器 |
enumerate() | 同时获取元素的索引和值,返回一个元组 (usize, T) |
消耗性方法(Consuming Adapter)
消耗型方法会遍历迭代器、消耗元素,并返回最终结果(触发惰性求值),常见方法:
| 方法 | 功能 |
|---|---|
collect<T>() | 收集元素为集合 T |
count() | 返回元素个数 |
fold(init, f) | 折叠元素(聚合计算),init 为初始值 |
any(f) | 存在元素满足 f, 则返回 true |
all(f) | 所有元素满足 f, 则返回 true |
find(f) | 查找第一个满足 f 的元素,返回 Option<Item> |
for_each(f) | 对每个元素执行 f,等价于 for循环 |
无限迭代器
无限迭代器(如 iter::repeat、Range::repeat),需通过 take(n) 等方法限制迭代次数,否则会陷入死循环。
常见无限迭代器:
iter::repeat(val):无限重复val;iter::cycle():循环迭代原迭代器(原迭代器需是Clone);(0..).step_by(n):无限递增序列(步长n)。
自定义迭代器
要创建自定义迭代器,只需实现 Iterator trait 的 next() 方法(其他方法为默认实现)。
Iterator trait 继承了 IntoIterator trait 的默认实现(IntoIterator::into_iter() 会直接返回迭代器本身(Self))。因此,只要一个类型实现了 Iterator::next(),就自动支持 for 循环遍历(本质是遍历迭代器自己)。
// pub trait Iterator {
// type Item;
//
// fn next(&mut self) -> Option<Self::Item>;
//
// // 其他很多默认实现的方法(map, filter, fold等)
// }struct Counter {count: u32,upper: u32,
}impl Counter {fn new(upper: u32) -> Counter {Counter { count: 0, upper }}
}impl Iterator for Counter {type Item = u32;fn next(&mut self) -> Option<Self::Item> {self.count += 1;if self.count <= self.upper {Some(self.count)} else {None}}
}pub fn it_test() {for n in Counter::new(5) {println!("{}", n); // 输出:1~5}
}
自定义容器
容器要被 for 循环遍历,需让容器(或其引用)实现 IntoIterator trait:
iter()-> 返回不可变引用的迭代器iter_mut()-> 返回可变引用的迭代器into_iter()-> 转移所有权迭代器
// for x in my_container(消耗所有权)
impl<T> IntoIterator for MyContainer<T> {type Item = T;type IntoIter = MyIntoIter<T>;fn into_iter(self) -> Self::IntoIter {...}
}// for x in &my_container(不可变引用)
impl<'a, T> IntoIterator for &'a MyContainer<T> {type Item = &'a T;type IntoIter = MyIter<'a, T>;fn into_iter(self) -> Self::IntoIter {...}
}// for x in &mut my_container(可变引用)
impl<'a, T> IntoIterator for &'a mut MyContainer<T> {type Item = &'a mut T;type IntoIter = MyIterMut<'a, T>;fn into_iter(self) -> Self::IntoIter {...}
}
以一个环形缓冲区为例,实现三种for操作:
use core::slice;
use std::mem::MaybeUninit;pub struct RingBuffer<T, const N: usize> {data: [MaybeUninit<T>; N],head: usize,len: usize,
}impl<T, const N: usize> RingBuffer<T, N> {pub fn new() -> Self {Self {data: unsafe { MaybeUninit::uninit().assume_init() },head: 0,len: 0,}}pub fn len(&self) -> usize {self.len}pub fn push(&mut self, value: T) -> Result<(), &str> {if self.len == N {return Err("buffer full");}let tail = (self.head + self.len) % N;self.data[tail] = MaybeUninit::new(value);self.len += 1;Ok(())}pub fn pop(&mut self) -> Option<T> {if self.len == 0 {return None;}let val = std::mem::replace(&mut self.data[self.head], MaybeUninit::uninit());self.head = (self.head + 1) % N;self.len -= 1;Some(unsafe { val.assume_init() })}/// 获取不可变切片视图(连续或分段)fn as_slices(&self) -> (&[T], &[T]) {unsafe {let head_ptr = self.data.as_ptr().add(self.head) as *const T;if self.head + self.len <= N {(slice::from_raw_parts(head_ptr, self.len), &[])} else {let first_len = N - self.head;let rest = (self.head + self.len) % N;(slice::from_raw_parts(head_ptr, first_len),slice::from_raw_parts(self.data.as_ptr() as *const T, rest),)}}}fn as_mut_slice(&mut self) -> (&mut [T], &mut [T]) {unsafe {let head_ptr = self.data.as_mut_ptr().add(self.head) as *mut T;if self.head + self.len <= N {(slice::from_raw_parts_mut(head_ptr, self.len), &mut [])} else {let first_len = N - self.head;let rest = (self.head + self.len) % N;(slice::from_raw_parts_mut(head_ptr, first_len),slice::from_raw_parts_mut(self.data.as_ptr() as *mut T, rest),)}}}
}impl<T, const N: usize> Drop for RingBuffer<T, N> {fn drop(&mut self) {for i in 0..self.len {let idx = (self.head + i) % N;unsafe {self.data[idx].assume_init_drop();}}}
}pub struct Iter<'a, T> {first: slice::Iter<'a, T>,second: slice::Iter<'a, T>,
}
impl<T, const N: usize> RingBuffer<T, N> {pub fn iter(&self) -> Iter<'_, T> {let (first, second) = self.as_slices();Iter {first: first.iter(),second: second.iter(),}}
}
impl<'a, T> Iterator for Iter<'a, T> {type Item = &'a T;fn next(&mut self) -> Option<Self::Item> {self.first.next().or_else(|| self.second.next())}
}pub struct IterMut<'a, T> {first: slice::IterMut<'a, T>,second: slice::IterMut<'a, T>,
}
impl<T, const N: usize> RingBuffer<T, N> {pub fn iter_mut(&mut self) -> IterMut<'_, T> {let (first, second) = self.as_mut_slice();IterMut {first: first.iter_mut(),second: second.iter_mut(),}}
}
impl<'a, T> Iterator for IterMut<'a, T> {type Item = &'a mut T;fn next(&mut self) -> Option<Self::Item> {self.first.next().or_else(|| self.second.next())}
}pub struct IntoIter<T, const N: usize> {buff: RingBuffer<T, N>,
}
impl<T, const N: usize> Iterator for IntoIter<T, N> {type Item = T;fn next(&mut self) -> Option<Self::Item> {self.buff.pop()}
}// IntoIterator Trait
impl<T, const N: usize> IntoIterator for RingBuffer<T, N> {type Item = T;type IntoIter = IntoIter<T, N>;fn into_iter(self) -> Self::IntoIter {IntoIter { buff: self }}
}
impl<'a, T, const N: usize> IntoIterator for &'a RingBuffer<T, N> {type Item = &'a T;type IntoIter = Iter<'a, T>;fn into_iter(self) -> Self::IntoIter {self.iter()}
}
impl<'a, T, const N: usize> IntoIterator for &'a mut RingBuffer<T, N> {type Item = &'a mut T;type IntoIter = IterMut<'a, T>;fn into_iter(self) -> Self::IntoIter {self.iter_mut()}
}pub fn ringbuff_test() {let mut buf: RingBuffer<i32, 4> = RingBuffer::new();buf.push(10).unwrap();buf.push(20).unwrap();buf.push(30).unwrap();println!("Iterate immutably:");for v in &buf {println!(" value = {v}");}println!("Iterate mutably:");for v in &mut buf {*v += 1;}println!("Iterate by value:");for v in buf {println!(" moved = {v}");}
}
