Rust中的Iterator和IntoIterator介绍及应用

Iterator即迭代器,它可以用于对数据结构进行迭代。被迭代的数据结构是可迭代的(iterable),所谓的可迭代就是这个数据结构有返回迭代器的方法,由于Rust的所有权机制,对一个数据结构的迭代器,有三种:

  1. 拿走数据结构的所有权的迭代器,在数据结构的方法上体现为into_iter(self)。在Rust中,into方法通常都是拿走所有权的,而且方法的入参self也表明了会拿走所有权,不会拿走所有权的是&self、&mut self这种入参。
  2. 不拿走数据结构的所有权,只读取数据结构内容的迭代器,在数据结构的方法上体现为iter(&self)。&self表明了只是只读借用。
  3. 不拿走数据结构的所有权,但是可以读写数据结构内容的迭代器,在数据结构的方法上体现为iter_mut(&mut self)

每调用一次数据结构的迭代器方法就会返回一个迭代器(拿走数据结构所有权的迭代器方法只能调用一次),迭代器迭代完了就不能继续使用了。

迭代器在Rust中是一个trait:

pub trait Iterator {
    /// The type of the elements being iterated over.
    /// 迭代器迭代的元素类型
    #[stable(feature = "rust1", since = "1.0.0")]
    type Item;

    /// Advances(向前移动) the iterator and returns the next value.
    ///
    /// Returns [`None`] when iteration is finished. Individual iterator
    /// implementations may choose to resume iteration, and so calling `next()`
    /// again may or may not eventually start returning [`Some(Item)`] again at some
    /// point.
    /// 迭代器取下一个元素的方法,如果没有下一个元素,会返回None
    /// 请注意next方法的入参是&mut self,也就是入参是可变的迭代器,为什么可变?因为迭代器内部通常都有
    /// 记录迭代进度的变量,比如数组下标这种,随着迭代的进行,变量会自增,所以需要改变迭代器的状态,用可变借用
    fn next(&mut self) -> Option<Self::Item>;

    /// 省略其它内容
}

你可以为你的数据结构实现Iterator trait,使得它可迭代。我们通常在for循环中使用迭代器。Rust标准库中的集合基本上都提供了返回迭代器的方法。比如我们最常用的Vec:

fn main() {
    // 下面是只读迭代器的使用
    let students = vec!["张三".to_string(),"李四".to_string(),"韩老二".to_string()];
    for student in &students{
        println!("&students写法:{}",student);
    }
    
    // Vec的iter()方法返回的是只读迭代器
    for student in students.iter(){
        println!("iter()方法调用写法:{}",student);
    }
    
    
    let mut ugly_girls = vec!["韩老二".to_string(),"叶慧三".to_string()];
    // for循环中的&mut ugly_girls写法等同于ugly_girls.iter_mut()
    for girl in &mut ugly_girls{
        girl.push_str("--really ");
    }
    // iter_mut()方法返回的是读写迭代器,可以对被迭代的元素进行修改
    for girl in ugly_girls.iter_mut(){
        girl.push_str("ugly");
    }
    
    println!("{:?}",ugly_girls);
    
    let ugly_boys = vec!["吴亦".to_string(),"肖障".to_string()];
    // for ugly_boy in ugly_boys等同于for ugly_boy in ugly_boys.into_iter
    for ugly_boy in ugly_boys {
        println!("{}",ugly_boy);
    }
    
    // 这儿不能再访问ugly_boys了,因为它的所有权在for循环的时候就被转移到迭代器中了
}

执行上述的代码后输出:

&students写法:张三
&students写法:李四
&students写法:韩老二
iter()方法调用写法:张三
iter()方法调用写法:李四
iter()方法调用写法:韩老二
["韩老二--really ugly", "叶慧三--really ugly"]
吴亦
肖障

好了,让我们来自己搞一个数据结构,然后为它实现三个迭代器方法加深理解。

假设我们有一个struct:

#[derive(Debug)]
pub struct SongList {
    // 歌单中歌曲列表
    pub songs: Vec<String>,
    // 歌单创建时间
    pub create_time: SystemTime,
}

表示歌单的数据结构,如果我们想要在for循环中去迭代歌单内容,我们当然可以直接通过SongList的songs字段进行迭代,但是这个是利用了Vec为我们实现好的迭代器,如果我们不暴露SongList内部的结构,把SongList当成一个黑盒去遍历,我们需要为它实现几个迭代器方法,每个方法返回一个迭代器,从而可以在for循环中通过迭代器来迭代SongList。

我们需要三个自定义的struct:Iter、IterMut、IntoIter分别表示SongList的三种迭代器,然后需要SongList提供三个方法分别返回三种迭代器:

impl SongList {
    fn iter(&self) -> Iter {
        Iter::new(self)
    }

    fn iter_mut(&mut self) -> IterMut {
        IterMut::new(self)
    }

    fn into_iter(self)->IntoIter{
        IntoIter::new(self)
    }
}

实现只读迭代器

// 只读迭代器,会用到引用,所以struct要带生命周期范型,这儿遵循Rust习惯,用'a表示生命周期参数
pub struct Iter<'a> {
    // 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'a
    pub song_list: &'a SongList,
    // 记录迭代进度的变量
    pub index: usize,
}

impl<'a> Iter<'a> {
    // 传入&SongList引用,就可以创建迭代器Iter
    fn new(song_list: &'a SongList) -> Iter {
        Iter {
            song_list,
            index: 0,
        }
    }
}

// 为Iter实现Iterator trait,从而让它变成真正的迭代器
impl<'a> Iterator for Iter<'a> {
    type Item = &'a String;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.song_list.songs.len() {
            let result = Some(&self.song_list.songs[self.index]);
            self.index += 1;
            result
        } else {
            None
        }
    }
}

// 为了让我们可以在for循环中通过&song_list来替代song_list.iter(),需要为&SongList实现IntoIterator
// 参考https://doc.rust-lang.org/std/iter/index.html中提到的内容
// If a collection type C provides iter(), it usually also implements IntoIterator for &C, 
// with an implementation that just calls iter(). Likewise, a collection C that provides iter_mut() 
// generally implements IntoIterator for &mut C by delegating to iter_mut(). This enables a convenient shorthand:
// 
// let mut values = vec![41];
// for x in &mut values { // same as `values.iter_mut()`
//     *x += 1;
// }
// for x in &values { // same as `values.iter()`
//     assert_eq!(*x, 42);
// }
// assert_eq!(values.len(), 1);
impl<'a> IntoIterator for &'a SongList {
    // 我们的SongList中被迭代的songs是Vec,所以这儿迭代的Item就是&String
    type Item = &'a String;
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}


impl SongList {
    fn iter(&self) -> Iter {
        Iter::new(self)
    }
}


fn main() {
    let song_list = SongList {
        songs: vec![
            "做个真的我".to_string(),
            "刀剑如梦".to_string(),
            "难念的经".to_string(),
            "任逍遥".to_string(),
        ],
        create_time: SystemTime::now(),
    };

    for song in song_list.iter() {
        println!("{}", song);
    }

    for song in &song_list {
        println!("{}", song);
    }

    println!("song_list:{:#?}", song_list);
}

实现可修改迭代器

// 读写迭代器
pub struct IterMut<'a> {
    // 要持有被迭代数据结构的可变应用
    song_list: &'a mut SongList,
    index: usize,
}

impl<'a> IterMut<'a> {
    // 将&mut SongList变成IterMut的方法
    fn new(song_list: &'a mut SongList) -> IterMut {
        IterMut {
            song_list,
            index: 0,
        }
    }
}

// 为IterMut实现Iterator trait,让它成为一个真正的迭代器
impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut String;
    fn next(& mut self) -> Option<Self::Item> {
        if self.index < self.song_list.songs.len() {
            let ptr = self.song_list.songs.as_mut_ptr();
            let result = Some(unsafe{&mut *ptr.add(self.index)});
            // 上面两行不能用下面这一行实现,
            // 否则会报错:error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
            // 参考:https://stackoverflow.com/questions/62361624/lifetime-parameter-problem-in-custom-iterator-over-mutable-references
            // let result = Some(&mut self.song_list.songs[self.index]);
            self.index += 1;
            result
        } else {
            None
        }
    }
}
// 为了让我们可以在for循环中通过&mut song_list来替代song_list.iter_mut(),需要为&mut SongList实现IntoIterator
impl<'a> IntoIterator for &'a mut SongList {
    type Item = &'a mut String;
    type IntoIter = IterMut<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter_mut()
    }
}


impl SongList {

    fn iter_mut(&mut self) -> IterMut {
        IterMut::new(self)
    }
}


fn main() {
    let mut zhangsan_song_list = SongList {
        songs: vec!["笑脸盈盈".to_string(), "我是一只鱼".to_string()],
        create_time: SystemTime::now(),
    };
    for song in &mut zhangsan_song_list{
        song.push_str("-真的");
    }

    for song in zhangsan_song_list.iter_mut() {
        song.push_str("-好好听啊");
    }

    println!("zhagnsan_song_list:{:#?}", zhangsan_song_list);
}

实现可以会拿走所有权的迭代器

// 定义一个会拿走所有权的迭代器struct
pub struct IntoIter{
    song_list:SongList
    // 不用index了,因为拿走所有权的话,从songs中拿走一个就少一个,不用再记录迭代进度了,迭代过的都被从Vec中移除了
}

impl IntoIter {
    // 将SongList直接消耗掉,变成IntoIter,传入的song_list变量的所有权就这样被转移到了迭代器中
    fn new(song_list:SongList)->Self{
       IntoIter{
           song_list
       }
    }
}

// 为IntoIter实现Iterator trait,让它成为一个真正的迭代器
impl Iterator for IntoIter {
    type Item = String;
    fn next(&mut self) -> Option<Self::Item> {
        // 从Vec中pop出的元素就没了,所以不需要我们额外定义index来记录迭代进度了
        self.song_list.songs.pop()
    }
}

// 为了让我们可以在for循环中通过for item in song_list来替代for item in song_list.into_iter(),需要为SongList实现IntoIterator
impl IntoIterator for SongList {
    type Item = String;
    type IntoIter = IntoIter;
    // 直接消耗掉SongList类型变量,将其所有权转移到迭代器中
    fn into_iter(self) -> Self::IntoIter {
        IntoIter::new(self)
    }
}

impl SongList {
    fn into_iter(self)->IntoIter{
        IntoIter::new(self)
    }
}

fn main() {
    let lisi_song_list = SongList{
        songs:vec!["天涯".to_string(),"死不了".to_string()],
        create_time:SystemTime::now()
    };

    //或者直接写成 for song in lisi_song_list{
    for song in lisi_song_list.into_iter(){
        println!("{}",song);
    }
}

完整代码:

use std::iter::Iterator;
use std::time::SystemTime;

#[derive(Debug)]
pub struct SongList {
    // 歌单中歌曲列表
    pub songs: Vec<String>,
    // 歌单创建时间
    pub create_time: SystemTime,
}

// 只读迭代器,会用到引用,所以struct要带生命周期范型,这儿遵循Rust习惯,用'a表示生命周期参数
pub struct Iter<'a> {
    // 迭代器本身不拥有被迭代的数据,而是引用被迭代的数据,所以需要定义引用,有人的地方就有江湖,同样有引用的地方就有生命周期'a
    pub song_list: &'a SongList,
    // 记录迭代进度的变量
    pub index: usize,
}

impl<'a> Iter<'a> {
    // 传入&SongList引用,就可以创建迭代器Iter
    fn new(song_list: &'a SongList) -> Iter {
        Iter {
            song_list,
            index: 0,
        }
    }
}

// 为Iter实现Iterator trait,从而让它变成真正的迭代器
impl<'a> Iterator for Iter<'a> {
    type Item = &'a String;
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.song_list.songs.len() {
            let result = Some(&self.song_list.songs[self.index]);
            self.index += 1;
            result
        } else {
            None
        }
    }
}

// 为了让我们可以在for循环中通过&song_list来替代song_list.iter(),需要为&SongList实现IntoIterator
// 参考https://doc.rust-lang.org/std/iter/index.html中提到的内容
// If a collection type C provides iter(), it usually also implements IntoIterator for &C,
// with an implementation that just calls iter(). Likewise, a collection C that provides iter_mut()
// generally implements IntoIterator for &mut C by delegating to iter_mut(). This enables a convenient shorthand:
//
// let mut values = vec![41];
// for x in &mut values { // same as `values.iter_mut()`
//     *x += 1;
// }
// for x in &values { // same as `values.iter()`
//     assert_eq!(*x, 42);
// }
// assert_eq!(values.len(), 1);
impl<'a> IntoIterator for &'a SongList {
    // 我们的SongList中被迭代的songs是Vec,所以这儿迭代的Item就是&String
    type Item = &'a String;
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

// 读写迭代器
pub struct IterMut<'a> {
    // 要持有被迭代数据结构的可变应用
    song_list: &'a mut SongList,
    index: usize,
}

impl<'a> IterMut<'a> {
    // 将&mut SongList变成IterMut的方法
    fn new(song_list: &'a mut SongList) -> IterMut {
        IterMut {
            song_list,
            index: 0,
        }
    }
}

// 为IterMut实现Iterator trait,让它成为一个真正的迭代器
impl<'a> Iterator for IterMut<'a> {
    type Item = &'a mut String;
    fn next(& mut self) -> Option<Self::Item> {
        if self.index < self.song_list.songs.len() {
            let ptr = self.song_list.songs.as_mut_ptr();
            let result = Some(unsafe{&mut *ptr.add(self.index)});
            // 上面两行不能用下面这一行实现,
            // 否则会报错:error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
            // 参考:https://stackoverflow.com/questions/62361624/lifetime-parameter-problem-in-custom-iterator-over-mutable-references
            // let result = Some(&mut self.song_list.songs[self.index]);
            self.index += 1;
            result
        } else {
            None
        }
    }
}
// 为了让我们可以在for循环中通过&mut song_list来替代song_list.iter_mut(),需要为&mut SongList实现IntoIterator
impl<'a> IntoIterator for &'a mut SongList {
    type Item = &'a mut String;
    type IntoIter = IterMut<'a>;
    fn into_iter(self) -> Self::IntoIter {
        self.iter_mut()
    }
}


// 定义一个会拿走所有权的迭代器struct
pub struct IntoIter{
    song_list:SongList
    // 不用index了,因为拿走所有权的话,从songs中拿走一个就少一个,不用再记录迭代进度了,迭代过的都被从Vec中移除了
}

impl IntoIter {
    // 将SongList直接消耗掉,变成IntoIter,传入的song_list变量的所有权就这样被转移到了迭代器中
    fn new(song_list:SongList)->Self{
       IntoIter{
           song_list
       }
    }
}

// 为IntoIter实现Iterator trait,让它成为一个真正的迭代器
impl Iterator for IntoIter {
    type Item = String;
    fn next(&mut self) -> Option<Self::Item> {
        // 从Vec中pop出的元素就没了,所以不需要我们额外定义index来记录迭代进度了
        self.song_list.songs.pop()
    }
}

// 为了让我们可以在for循环中通过for item in song_list来替代for item in song_list.into_iter(),需要为SongList实现IntoIterator
impl IntoIterator for SongList {
    type Item = String;
    type IntoIter = IntoIter;
    // 直接消耗掉SongList类型变量,将其所有权转移到迭代器中
    fn into_iter(self) -> Self::IntoIter {
        IntoIter::new(self)
    }
}

impl SongList {
    fn iter(&self) -> Iter {
        Iter::new(self)
    }

    fn iter_mut(&mut self) -> IterMut {
        IterMut::new(self)
    }

    fn into_iter(self)->IntoIter{
        IntoIter::new(self)
    }
}


fn main() {
    let song_list = SongList {
        songs: vec![
            "做个真的我".to_string(),
            "刀剑如梦".to_string(),
            "难念的经".to_string(),
            "任逍遥".to_string(),
        ],
        create_time: SystemTime::now(),
    };

    for song in song_list.iter() {
        println!("{}", song);
    }

    for song in &song_list {
        println!("{}", song);
    }

    println!("song_list:{:#?}", song_list);

    let mut zhangsan_song_list = SongList {
        songs: vec!["笑脸盈盈".to_string(), "我是一只鱼".to_string()],
        create_time: SystemTime::now(),
    };
    for song in &mut zhangsan_song_list{
        song.push_str("-真的");
    }

    for song in zhangsan_song_list.iter_mut() {
        song.push_str("-好好听啊");
    }

    println!("zhagnsan_song_list:{:#?}", zhangsan_song_list);

    let lisi_song_list = SongList{
        songs:vec!["天涯".to_string(),"死不了".to_string()],
        create_time:SystemTime::now()
    };

    //或者直接写成 for song in lisi_song_list{
    for song in lisi_song_list.into_iter(){
        println!("{}",song);
    }
}

运行的控制台输出:

做个真的我
刀剑如梦
难念的经
任逍遥
做个真的我
刀剑如梦
难念的经
任逍遥
song_list:SongList {
    songs: [
        "做个真的我",
        "刀剑如梦",
        "难念的经",
        "任逍遥",
    ],
    create_time: SystemTime {
        tv_sec: 1690101338,
        tv_nsec: 120053000,
    },
}
zhagnsan_song_list:SongList {
    songs: [
        "笑脸盈盈-真的-好好听啊",
        "我是一只鱼-真的-好好听啊",
    ],
    create_time: SystemTime {
        tv_sec: 1690101338,
        tv_nsec: 120146000,
    },
}
死不了
天涯

你可能感兴趣的:(rust学习,rust,迭代器)