rust十三.2、迭代器

Iterator(迭代器)是一个比较常见的概念,大部分语言都有。大部分语言实现迭代器的逻辑并没有特别的。

看完了有关内容,作者的意思是:rust的迭代器和匿名函数一样,都是为了提供时下流行的函数式编程。

此二者为rust实现零成本抽象提供了不少的贡献。

本部分概念比较多,如果是编程初学者可能会有点吃力,如果是老手还是比较容易理解的。

一、迭代器基本概念

 问题:

  1. 迭代器是什么
  2. 如何创建
  3. 迭代器佑什么用

1.1、迭代器是什么

迭代器:能够逐个获取集合元素(以集合元素居多,实际可能不一定是集合)并指向下一个元素的对象(一段代码)

迭代器是一种简单抽象,无论其代码如何实现,但本质上差不多:一个next方法(叫什么无所谓),一个指示器(也许更多,但大概就是这个意思)+相关数据存储结构

1.2、如何创建

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    // 此处省略了方法的默认实现
}

 一个对象(结构或者什么的)只要实现这个特质就可以创建迭代器。

例如以下为特定的结构创建一个迭代器:

/**
 * 
 */
struct  Books{
    names:String,
    author:String,
    content:String
}
impl Books {
    fn iter(&mut self) -> BooksIterator {      
        BooksIterator{
            current_index:0,
            strs:vec![self.names.clone(), self.author.clone(),self.content.clone()]
        }  
    }
}

/**
 * 关键之一:定义一个临时结构,以便迭代器能够访问结构中的字段,并进行查找
 */
struct BooksIterator{
    current_index:usize,
    strs:Vec<String>
}

/**
 * 实现迭代器特质:Iterator
 */
impl Iterator for BooksIterator {
    type Item = String;
    fn next(&mut self) -> Option<String> {
        if self.current_index < 3 {
            let item = self.strs[self.current_index].clone();
            self.current_index += 1;
            Some(item)
        } else {
            None
        }
    }
}   

fn main() {
    let mut b = Books{
        names:String::from("星星之火可以燎原"),
        author:String::from("毛->泽东"),
        content:String::from("新年已经到来几天了,你的信我还没有回答。
        一则因为有些事情忙着,二则也因为我到底写点什么给你呢?
        有什么好一点的东西可以贡献给你呢?
        搜索我的枯肠,没有想出一点什么适当的东西来,因此也就拖延着")        
    };
    let it=b.iter();
    for i in it {
        println!("{}",i);
    }
}

幸运的是(当然也是应该的),rust为许多应该提供迭代器的对象提供了创建迭代器的方法。

例如向量

let mut height_arr: Vec<i32> = Vec::new();
for i in height_arr.iter() {
        println!("{}", i);
}

 rust为向量实现了多个方法用于创建迭代器:

  1. iter  - 生成一个不可变引用的迭代器
  2. into_iter  --生成一个获取所有权的迭代器
  3. iter_mut  --生成一个可变引用的迭代器

例如数组

fn print_use_for_loop_3_iter(arr:&[u32]){
    //注意这个是针对固定数组,如果是可变数组则可以
    //for element in array.into_iter()
    
    println!("---- 使用 for 循环和迭代iter 打印数组元素");    
    for ele in arr.iter() {  
       print!("{}", ele);  
    } 
    println!("\n");
}

 

1.3、两个重要概念:迭代器适配器消费适配器

迭代器适配器-基于生成迭代器方法产生新的迭代器的方法,也可以称为迭代器包装器

消费适配器-基于迭代器,消费迭代器的方法。目的是基于迭代器产生一些新的结果(非迭代器)

此二者都是方法,通常都属于迭代器自身的方法。迭代器适配器生成新的迭代器,消费适配器则是使用迭代器。

原书给出了很完善和浅显易懂的例子:

-- 迭代器适配器
let v1: Vec<i32> = vec![1, 2, 3];
v1.iter().map(|x| x + 1);

-- 消费适配器
fn iterator_sum() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1.iter();
    let total: i32 = v1_iter.sum();
    assert_eq!(total, 6);
}

1.4、用处

  • 核心用处:源码看起来更加简洁一些,因为可以向所谓的函数式编程靠近。
  • 次要作用:让工程师专注于业务逻辑,因为数据怎么得到,由迭代器处理。 此作用因人而异

函数式编程有优点有缺点,这是一个编程风格。如果你不喜欢,不是非得用不可。

阻碍项目进度的往往不是这些细枝末节,而是诸如需求,设计、测试。

如果一个项目完全不用迭代器也是完全很好的。

 

不过如果rust官方已经提供了很多的适配器,那么该用还是要用的。

例如上文的消费适配器sum,为什么不用了?毕竟可以少打几个字母。

二、性能比较(对比循环)

结论:请放心大胆的使用迭代器和闭包吧!它们使得代码看起来更高级,但并不为此引入运行时性能损失

这是因为rust的编译器对匿名函数和迭代器的实现进行了优化。按照作者的意思就是:在很多环境中,迭代相对循环所损失的性能微乎其微。

2.1、零成本抽象

zero-cost abstractions,中文含义“零成本抽象”

意思就是:虽然我把代码写得更加简洁(往往可能就是抽象了),但是我通过编译器的努力,不会因为这个抽象而损失性能,或者即使有损失,也是很小的。

这个所谓的零成本抽象应该是rust的目标之一。

把语法搞得复杂抽象应该也是rust的目标之一,结合零抽象成本,可以告诉工程师放心使用rust。

2.2、rust编译器

我已经说了很多次:rust厉害的是编译器。大体是这个意思。

具体怎么零成本的,作者没有多说,不过提到一个概念:unroll(展开)

这是一个“笨”办法,也是提升性能的最常用的一个方法。

这个方法就是把代码编译为多段重复的代码,大概如下:

/**
 * 循环,非展开
 */
fn roll(){
    let arr=[1,2,3];
    let mut total=0;
    for i in 0..arr.len(){
        total=total+arr[i];
    }
    println!("{}",total);
}
/**
 * 展开测试1
 */
fn unroll_one(){
    let arr=[1,2,3];
    let mut total=0;
    total=total+arr[0]+arr[1]+arr[2];
    println!("{}",total);
}
/**
 * 展开测试2
 */
fn unroll_two(){
    let a=1;
    let b=2;
    let c=3;
    let mut total=0;
    total=a+b+c;
    println!("{}",total);
}

fn main(){
    roll();
    unroll_one();
    unroll_two();
}

 rust大概会把上文中的roll函数改写为 unroll_two之类的代码。当然这仅仅是一个示意,本人没有研究过rustc的实现。

写程序要看是出于什么目的,如果追求工程效率,这样写肯定被骂死。

如果追求性能,这反而是相当明智有效的方法之一。

2.3、个人看法

  1. 如果有rust的默认迭代器,我会考虑用用。如果没有,我不会特意去定义的
  2. 出于性能等原因,我更愿意使用循环,而不是迭代
  3. 希望rust团队能给rust添加更多面向对象的内容。面向对象并不会对性能造成什么影响

三、小结

  1. 实现特质Iterator就能够创建自己的迭代器
  2. rust提供了许多默认的迭代器适配器、迭代器方法
  3. 利用消费适配器、迭代器适配器和匿名函数,能够实现所谓的函数式编程
  4. 据说rust能够让迭代器的性能和for循环差不多
  5. 迭代器是rust实现其零成本抽现象的实践之一

 

posted @ 2024-12-10 19:14  正在战斗中  阅读(38)  评论(0编辑  收藏  举报