Skip to content

Instantly share code, notes, and snippets.

@soyart
Created January 24, 2023 20:18
Show Gist options
  • Select an option

  • Save soyart/9b3c30145a9a04514e83b94398589282 to your computer and use it in GitHub Desktop.

Select an option

Save soyart/9b3c30145a9a04514e83b94398589282 to your computer and use it in GitHub Desktop.
Basic Rust `Iterator` and `DoubleEndedIterator` implementation on nested flattener.
#![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