Created
January 24, 2023 20:18
-
-
Save soyart/9b3c30145a9a04514e83b94398589282 to your computer and use it in GitHub Desktop.
Basic Rust `Iterator` and `DoubleEndedIterator` implementation on nested flattener.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![allow(dead_code)] | |
| // Basic Rust `Iterator` and `DoubleEndedIterator` implementation on nested flattener. | |
| struct Flattener<O> | |
| where | |
| O: Iterator, | |
| O::Item: IntoIterator, | |
| { | |
| outter: O, | |
| inner: Option<<O::Item as IntoIterator>::IntoIter>, | |
| } | |
| impl<O> Flattener<O> | |
| where | |
| O: Iterator, | |
| O::Item: IntoIterator, | |
| { | |
| fn new<T>(iterable: T) -> Flattener<O> | |
| where | |
| T: IntoIterator<IntoIter = O>, | |
| { | |
| Flattener { | |
| outter: iterable.into_iter(), | |
| inner: None, | |
| } | |
| } | |
| } | |
| impl<O> Iterator for Flattener<O> | |
| where | |
| O: Iterator, | |
| O::Item: IntoIterator, | |
| { | |
| type Item = <O::Item as IntoIterator>::Item; | |
| fn next(&mut self) -> Option<Self::Item> { | |
| // Return items in current inner iterator, if there's Some(inner_iter) | |
| if let Some(ref mut inner_iter) = self.inner { | |
| // Return Some(item) from inner_iter | |
| if let Some(item) = inner_iter.next() { | |
| return Some(item); | |
| } | |
| // If there's no such item from inner_iter, we remove it from self.inner, | |
| // making way for new inner iter from self.outter.next() | |
| self.inner = None; | |
| } | |
| // Otherwise, we get new inner iterator from outter | |
| if let Some(inner) = self.outter.next() { | |
| self.inner = Some(inner.into_iter()); | |
| } | |
| self.inner.as_mut()?.next() | |
| } | |
| } | |
| // If O is DoubleEndedIterator and <O as Iterator>::Item is IntoIterator, | |
| // and <O::Item as IntoIterator>::IntoIter is DoubleEndedIterator, | |
| // then Flattener<O> too implements DoubleEndedIterator. | |
| impl<O> DoubleEndedIterator for Flattener<O> | |
| where | |
| O: DoubleEndedIterator, | |
| <O as Iterator>::Item: IntoIterator, | |
| <O::Item as IntoIterator>::IntoIter: DoubleEndedIterator, | |
| { | |
| // The return type Self::Item here refers to <Self as Iterator>::Item, | |
| // since DoubleEndedIterator extends Iterator. | |
| fn next_back(&mut self) -> Option<Self::Item> { | |
| // Return items in current inner iterator, if there's Some(inner_iter) | |
| if let Some(ref mut inner_iter) = self.inner { | |
| // Return Some(item) from inner_iter | |
| if let Some(item) = inner_iter.next_back() { | |
| return Some(item); | |
| } | |
| // If there's no such item from inner_iter, we remove it from self.inner, | |
| // making way for new inner iter from self.outter.next() | |
| self.inner = None; | |
| } | |
| // Otherwise, we get new inner iterator from outter | |
| if let Some(inner) = self.outter.next_back() { | |
| self.inner = Some(inner.into_iter()); | |
| } | |
| self.inner.as_mut()?.next_back() | |
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| fn test_vec_usize<T>(v: Vec<Vec<T>>, expected_len: usize) { | |
| let r: Vec<T> = (Flattener::new(v)).collect(); | |
| assert_eq!(r.len(), expected_len); | |
| } | |
| fn test_generic_iter<O>(o: O, expected_len: usize) | |
| where | |
| O: DoubleEndedIterator, | |
| O::Item: IntoIterator, | |
| { | |
| let f = Flattener::new(o); | |
| let r: Vec<<O::Item as IntoIterator>::Item> = f.collect(); | |
| assert_eq!(r.len(), expected_len) | |
| } | |
| #[test] | |
| fn empty() { | |
| let v: Vec<Vec<()>> = vec![vec![], vec![], vec![]]; | |
| test_vec_usize(v.clone(), 0); | |
| test_generic_iter(v.into_iter(), 0) | |
| } | |
| #[test] | |
| fn not_empty() { | |
| let v: Vec<Vec<usize>> = vec![vec![1, 2, 3], vec![], vec![]]; | |
| test_vec_usize(v.clone(), 3); | |
| test_generic_iter(v.clone().into_iter(), 3); | |
| test_generic_iter(&mut v.into_iter(), 3); | |
| } | |
| #[test] | |
| fn wide() { | |
| let v: Vec<Vec<usize>> = vec![vec![1, 2], vec![3], vec![4]]; | |
| test_vec_usize(v.clone(), 4); | |
| test_generic_iter(v.clone().into_iter(), 4); | |
| test_generic_iter(&mut v.into_iter(), 4); | |
| } | |
| #[test] | |
| fn hash_map() { | |
| use std::collections::HashMap; | |
| let mut m = HashMap::new(); | |
| m.insert("foo".to_string(), vec!['f', 'o', 'o']); | |
| m.insert("bar".to_string(), vec!['b', 'a', 'r']); | |
| m.insert("2000".to_string(), vec!['2', '0', '0', '0']); | |
| let _iter = m.values(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment