rust迭代器

迭代器用来遍历容器。

迭代器就是把容器中的所有元素按照顺序一个接一个的传递给处理逻辑。

Rust中的迭代器

标准库中定义了Iterator特性

trait Iterator {
     type Item;
     fn next(&mut self) -> Option<Self::Item>;
}

实现了Iterator特性的结构体就是迭代器。

很多类型都有iter()方法,返回一个Iter结构体,该结构体实现了Iterator特性。

一、创建迭代器

Rust中有三种类型的迭代器。

从容器创建迭代器有三种方法。

iter() 返回一个只读可重入迭代器,迭代器元素的类型为 &T

iter_mut() 返回一个可写可重入迭代器,迭代器元素的类型为 &mut T

into_iter() 返回一个只读不可重入迭代器,迭代器元素的类型为 T

(一)iter()

返回一个只读可重入迭代器,迭代器元素的类型为&T。

例子

fn main() {
    let names = vec!["简单教程", "简明教程", "简单编程"];
    let name = names.iter();
    println!("{:?}", names);
}

(二)iter_mut()

返回一个可写可重入迭代器,迭代器元素的类型为&mut T。

例子

fn main() {
    let mut names = vec!["简单教程", "简明教程", "简单编程"];
    let name = names.iter_mut();
    println!("{:?}", names);
}

(三)into_iter()

返回一个只读不可重入迭代器,迭代器元素的类型为T

它会把容器的值移动到迭代器中。

iter_into()之后的容器不可重用。

例子

fn main() {
    let mut names = vec!["简单教程", "简明教程", "简单编程"];
    let name = names.into_iter();
    println!("{:?}", name);
    //println!("{:?}", names);//去掉注释会编译错误
}

二、使用迭代器

(一)返回迭代器元素个数

count()

消耗迭代器,计算迭代次数并返回它。

此方法将反复调用next(),直到遇到None,并返回它看到Some的次数。 请注意,即使迭代器没有任何元素,也必须至少调用一次next()

溢出行为

该方法无法防止溢出,因此对具有超过usize::MAX个元素的迭代器的元素进行计数会产生错误的结果或panics。

如果启用了调试断言,则将保证panic。

Panics

如果迭代器具有多个usize::MAX元素,则此函数可能为panic。

例子

let a = [1, 2, 3];
assert_eq!(a.iter().count(), 3);

(二)访问迭代器元素

nth(n)

返回迭代器的第n个元素。

计数从零开始,因此nth(0) 返回第一个值,nth(1) 返回第二个值,依此类推。

请注意,所有先前的元素以及返回的元素都将从迭代器中消耗。这意味着前面的元素将被丢弃,并且在同一迭代器上多次调用nth(0)将返回不同的元素。

如果n大于或等于迭代器的长度,则nth()将返回None。

例子

let a = [1, 2, 3];
assert_eq!(a.iter().nth(1), Some(&2));
多次调用nth() 不会回退迭代器:
let a = [1, 2, 3];
let mut iter = a.iter();
assert_eq!(iter.nth(1), Some(&2));
assert_eq!(iter.nth(1), None);
如果n大于或等于迭代器的长度,则返回None:
let a = [1, 2, 3];
assert_eq!(a.iter().nth(10), None);

last()

消耗迭代器,返回最后一个元素。

此方法将评估迭代器,直到返回None。 这样做时,它会跟踪当前元素。 返回None之后,last() 将返回它看到的最后一个元素。

例子

let a = [1, 2, 3];
assert_eq!(a.iter().last(), Some(&3));

(三)遍历迭代器

1.使用next()方法遍历容器

例子

fn main() {
     let a = [10,20,30];
     let mut iter = a.iter(); // 从一个数组中返回迭代器
     println!("{:?}",iter);
     //使用next() 方法返回迭代器中的下一个元素
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
}
编译运行结果如下
Iter([10, 20, 30])
Some(10)
Some(20)
Some(30)
None

2.使用for循环遍历

手动调用next()太麻烦了,推荐使用for循环来使用迭代器。

(1)iter()

例子

fn main() {
    let names = vec!["简单教程", "简明教程", "简单编程"];
    for name in names.iter() {
         println!("{}", name);
    }
    println!("{:?}", names);
}

for name in &names

实际上等价于

for name in names.iter()

(2)iter_mut()

例子

fn main() {
    let mut names = vec!["简单教程", "简明教程", "简单编程"];
    for name in names.iter_mut() {
         println!("{}", name);
    }
    println!("{:?}", names);
}

for name in &mut names

实际上等价于

for name in names.iter_mut()

(3)into_iter()

例子

fn main() {
    let names = vec!["简单教程", "简明教程", "简单编程"];
    for name in names.into_iter() {
         println!("{}", name);
    }
    //println!("{:?}", names);     //去掉注释会编译错误
}

for name in names

实际上等价于

for name in names.into_iter()

(四)迭代器转换成容器

collect()

将迭代器转换为容器。

使用collect()的最基本模式是将一个容器转换为另一个容器。 在一个容器上调用iter,进行了一堆转换,最后调用collect()。

由于collect()非常通用,因此可能导致类型推断问题。 因此,要指定类型或使用turbofish语法。

例子

let a = [1, 2, 3];
let doubled: Vec<i32> = a.iter().map(|&x| x * 2).collect();
assert_eq!(vec![2, 4, 6], doubled);
使用自动推断类型
let a = [1, 2, 3];
let doubled: Vec<_> = a.iter().map(|&x| x * 2).collect();
assert_eq!(vec![2, 4, 6], doubled);
使用turbofish
let a = [1, 2, 3];
let doubled = a.iter().map(|x| x * 2).collect::<Vec<i32>>();
assert_eq!(vec![2, 4, 6], doubled);
_ 与turbfish一起使用:
let a = [1, 2, 3];
let doubled = a.iter().map(|x| x * 2).collect::<Vec<_>>();
assert_eq!(vec![2, 4, 6], doubled);

(五)迭代器转换

map()方法可以转换迭代器

创建一个迭代器,该迭代器每个元素是由原迭代器元素应用闭包得到。

可以这样考虑map(): 如果您有一个元素类型为A的迭代器,您想要元素类型为B的迭代器,则可以使用map(),传递一个把A转成B的闭包。

例子

let a = [1, 2, 3];
let mut iter = a.iter().map(|x| 2 * x);
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), Some(4));
assert_eq!(iter.next(), Some(6));
assert_eq!(iter.next(), None);

如果您正在做某种副作用,请首选for而不是map():

不要这样做:

(0..5).map(|x| println!("{x}"));// 它甚至不会执行,因为它很懒。Rust会就此警告您。

而是用:

for x in 0..5 {
     println!("{x}");
}

flat_map()

创建一个迭代器,其工作方式类似于map,但它会将嵌套的结构展平。

map非常有用,但仅当闭包产生值时才使用。 如果它产生一个迭代器,则存在一个额外的间接层。flat_map() 将自行删除这个间接层。

您可以把flat_map(f) 视为map(f).flatten()。

map的闭包为每个元素返回一个值,而flat_map ()的闭包为每个元素返回一个迭代器。

例子

let words = ["alpha", "beta", "gamma"];
// chars() 返回一个迭代器
let merged: String = words.iter().flat_map(|s| s.chars()).collect();
assert_eq!(merged, "alphabetagamma");

flatten()

创建一个去掉嵌套层的迭代器。比如二维向量变一维向量

例子

let data = vec![vec![1, 2, 3, 4], vec![5, 6]];
let flattened = data.into_iter().flatten().collec::<Vec<u8>>();
assert_eq!(flattened, &[1, 2, 3, 4, 5, 6]);

let words = ["alpha", "beta", "gamma"];
// chars() 返回一个迭代器
let merged: String = words.iter().map(|s| s.chars()).flatten().collect();
assert_eq!(merged, "alphabetagamma");
您也可以用flat_map()来重写它
let words = ["alpha", "beta", "gamma"];
// chars() 返回一个迭代器
let merged: String = words.iter().flat_map(|s| s.chars()).collect();
assert_eq!(merged, "alphabetagamma");

展平一次只能删除一层嵌套:

let d3 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]];
let d2 = d3.iter().flatten().collect::<Vec<_>>();
assert_eq!(d2, [&[1, 2], &[3, 4], &[5, 6], &[7, 8]]);
let d1 = d3.iter().flatten().flatten().collect::<Vec<_>>();
assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]);

在这里,我们看到flatten()仅删除了一层嵌套。三维数组变二维而不是一维。要获得一维,您必须再次flatten()。

(六)enumerate

创建一个迭代器,该迭代器元素是(i, val),其中i是当前迭代索引,val是迭代器返回的值。

enumerate()保持其计数为usize。

溢出行为

该方法无法防止溢出,因此枚举多个usize::MAX元素会产生错误的结果或panics。 如果启用了调试断言,则将保证panic。

Panics

如果要返回的索引将溢出usize,则返回的迭代器可能为panic。

例子

let a = ['a', 'b', 'c'];
let mut iter = a.iter().enumerate();
assert_eq!(iter.next(), Some((0, &'a')));
assert_eq!(iter.next(), Some((1, &'b')));
assert_eq!(iter.next(), Some((2, &'c')));
assert_eq!(iter.next(), None);

(七)逆转迭代器

rev

反转迭代器的方向。

通常,迭代器从左到右进行迭代。 使用rev() 之后,迭代器将改为从右向左进行迭代。

例子

let a = [1, 2, 3];
let mut iter = a.iter().rev();
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), None);

(八)截取迭代器

take(n)

创建一个迭代器,它的元素是原迭代器的前n个元素。如果原迭代器元素数小于n,则返回原迭代器所有元素。

例子

let a = [1, 2, 3];
let mut iter = a.iter().take(2);
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), None);
let v = [1, 2];
let mut iter = v.into_iter().take(5);
assert_eq!(iter.next(), Some(1));
assert_eq!(iter.next(), Some(2));
assert_eq!(iter.next(), None);

(九)其他

max

返回迭代器的最大元素。

如果几个元素最大相等,则返回最后一个元素。如果迭代器为空,则返回None。

请注意,由于NaN不可比较,f32/f64没有实现Ord。 您可以使用Iterator::reduce解决此问题:

assert_eq!([2.4, f32::NAN, 1.3].into_iter().reduce(f32::max).unwrap(),2.4);

例子

let a = [1, 2, 3];
let b: Vec = Vec::new();
assert_eq!(a.iter().max(), Some(&3));
assert_eq!(b.iter().max(), None);

min

返回迭代器的最小元素。

如果几个元素相等地最小,则返回第一个元素。 如果迭代器为空,则返回None。

请注意,由于NaN不可比较,f32/f64没有实现Ord。您可以使用Iterator::reduce解决此问题:

assert_eq!([2.4, f32::NAN, 1.3].into_iter().reduce(f32::min).unwrap(),1.3);

例子

let a = [1, 2, 3];
let b: Vec = Vec::new();
assert_eq!(a.iter().min(), Some(&1));
assert_eq!(b.iter().min(), None);

sum

对迭代器的元素求和。

获取每个元素,将它们添加在一起,然后返回结果。

空的迭代器将返回该类型的零值。

sum()可用于对任何实现Sum的类型求和,包括Option和Result。

Panics

当调用sum() 并返回原始整数类型时,如果计算溢出并且启用了调试断言,则此方法将为panic。

例子

let a = [1, 2, 3];
let sum: i32 = a.iter().sum();
assert_eq!(sum, 6);
相关推荐
zzb15801 小时前
项目集成Spring Security认证部分
java·后端·spring
行路见知3 小时前
1.5 Go切片使用
开发语言·golang
美味小鱼4 小时前
如何在Windows、Linux和macOS上安装Rust并完成Hello World
rust·hello world
子燕若水5 小时前
uv 安装包
开发语言·chrome·python
不会打代码呜呜呜呜6 小时前
小白零基础--CPP多线程
开发语言·c++·算法
涛ing6 小时前
【5. C++ 变量作用域及其深入探讨】
java·linux·c语言·开发语言·c++·ubuntu·vim
Source.Liu6 小时前
11 3D变换模块(transform3d.rs)
rust·cad·euclid
码农小旋风6 小时前
Hive分区和分桶
后端
Hi Man7 小时前
Python之如何在Visual Studio Code 中写的python程序打包成可以在Windows系统下运行的.exe程序
开发语言·vscode·python
CHANG_THE_WORLD7 小时前
C++并发编程指南04
开发语言·c++