美文网首页
Rust for cpp devs - 迭代器

Rust for cpp devs - 迭代器

作者: 找不到工作 | 来源:发表于2021-05-14 18:16 被阅读0次

    迭代器(Iterator)可以允许对序列中的每一个元素执行某个操作。

    Rust 的迭代器分为三种:

    • iter() 返回一个常量引用,不可改变元素的值
    • iter_mut() 返回一个可变引用,能够修改元素的值
    • into_iter() 会拿走容器的 ownership

    Iterator Trait

    迭代器实现了 Iterator trait,定义如下(type Item 的用法在后面例子中会讲):

    pub trait Iterator {
        type Item;
    
        fn next(&mut self) -> Option<Self::Item>;
    
        // methods with default implementations elided
    }
    

    Iterator trait 要求必须实现 next 方法,它返回一个封装在 Some 中的元素,如果没有下一个元素,则返回 None。之所以要求必须实现 next,是因为 Iterator 默认实现的某些其他方法会调用。

    例如:

    #[cfg(test)]
    mod tests {
        #[test]
        fn iterator_demonstration() {
            let v1 = vec![1, 2, 3];
    
            let mut v1_iter = v1.iter();
    
            assert_eq!(v1_iter.next(), Some(&1));
            assert_eq!(v1_iter.next(), Some(&2));
            assert_eq!(v1_iter.next(), Some(&3));
            assert_eq!(v1_iter.next(), None);
        }
    }
    

    值得注意的是迭代器变量定义为 mut,因为每次调用 next 都会修改迭代器指向下一个元素。

    消耗 Iterator

    Iterator trait 中调用 next 的方法称为 consuming adaptors。因为使用这些方法会“耗尽”这个迭代器,例如 sum。由于迭代器都是惰性的,必须使用 consuming adaptors 才能得到运行结果

        #[test]
        fn iterator_sum() {
            let v1 = vec![1, 2, 3];
            let v1_iter = v1.iter();
            let total: u32 = v1_iter.sum();
            assert_eq!(total, 6);
        }
    

    此后无法再使用 v1_iter,因为sum 已经拿走了它的 ownership。

    生成 Iterator

    除了消耗 Iterator 的, 也有生成 Iterator 的方法,称为 iterator adaptors。但是生成后的 Iterator 上必须使用 consuming adaptors 来得到结果。

    例如,map 方法可以将旧的 iterator 映射到一个新的 iterator。下面的代码将 v1 中的每个元素加 1 并生成一个 v2。注意,map 只是做了 iterator 的映射,并不是直接把 v1 变成 v2

        #[test]
        fn iterator_map() {
            let v1 = vec![1, 2, 3];
            let v1_iter = v1.iter();
            let v2_iter = v1_iter.map(|x| {x + 1});
            let v2: Vec<_> = v2_iter.collect();
    
            assert_eq!(v2, vec![2, 3, 4]);
        }
    

    还有一个常用的 iterator adapter 是 filter 方法:

    #[derive(PartialEq, Debug)]
    struct Shoe {
        size: u32,
        style: String,
    }
    
    fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
        shoes.into_iter().filter(|s| s.size == shoe_size).collect()
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn filters_by_size() {
            let shoes = vec![
                Shoe {
                    size: 10,
                    style: String::from("sneaker"),
                },
                Shoe {
                    size: 13,
                    style: String::from("sandal"),
                },
                Shoe {
                    size: 10,
                    style: String::from("boot"),
                },
            ];
    
            let in_my_size = shoes_in_my_size(shoes, 10);
    
            assert_eq!(
                in_my_size,
                vec![
                    Shoe {
                        size: 10,
                        style: String::from("sneaker")
                    },
                    Shoe {
                        size: 10,
                        style: String::from("boot")
                    },
                ]
            );
        }
    }
    

    shoes_in_my_size 函数筛选出所有大小为 10 的鞋。filter 也是一个从 iterator 到 iterator 的映射。在这里,我们使用 into_iter 拿走了原来的容器的 ownership。

    自定义 Iterator

    我们定义了一个计数器类:

    struct Counter {
        count: u32,
    }
    
    impl Counter {
        fn new() -> Counter {
            return Counter{count: 0};
        }
    }
    

    然后让它实现 Iterator trait,计数到 5 终止:

    impl Iterator for Counter {
        type Item = u32;  // call to next() will return Option<u32>
    
        fn next(&mut self) -> Option<Self::Item> {
            if self.count < 5 {
                self.count += 1;
                return Some(self.count);
            }
    
            return None;  // return None when iteration ends
        }
    }
    

    这里值得注意的点已经在注释中标出。测试代码如下:

        #[test]
        fn test_counter() {
            let mut counter = Counter::new();
    
            assert_eq!(counter.next(), Some(1));
            assert_eq!(counter.next(), Some(2));
            assert_eq!(counter.next(), Some(3));
            assert_eq!(counter.next(), Some(4));
            assert_eq!(counter.next(), Some(5));
            assert_eq!(counter.next(), None);
        }
    

    相关文章

      网友评论

          本文标题:Rust for cpp devs - 迭代器

          本文链接:https://www.haomeiwen.com/subject/sjzfjltx.html