Skip to content

Instantly share code, notes, and snippets.

@Zoybean
Created April 6, 2022 13:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zoybean/808d9203644d4fd22842e381c0cc7a6f to your computer and use it in GitHub Desktop.
Save Zoybean/808d9203644d4fd22842e381c0cc7a6f to your computer and use it in GitHub Desktop.
#![feature(cell_update)]
#[cfg(test)]
mod test {
use crate::*;
use std::cell::Cell;
#[test]
fn test() {
test_counter::<0>();
test_counter::<1>();
test_counter::<2>();
test_counter::<3>();
test_counter::<4>();
test_counter::<5>();
test_counter::<1000>();
}
fn test_counter<const N: usize>() {
let c = Cell::new(0);
let cc = Counts(&c);
let _ = [(); N].into_iter().zip_clones(cc).for_each(|_| {});
// 1 fewer clone than the number of items in the collection
assert_eq!(N.saturating_sub(1), c.get());
}
struct Counts<'a>(&'a Cell<usize>);
impl<'a> Counts<'a> {
fn new(counter: &'a Cell<usize>) -> Self {
Self(counter)
}
}
impl<'a> Clone for Counts<'a> {
fn clone(&self) -> Self {
self.0.update(|x| x + 1);
Self::new(self.0)
}
}
}
use std::iter::Peekable;
use std::mem;
pub trait ZipClones<E> {
fn zip_clones<T>(self, to_clone: E) -> Cloner<Self, E>
where
Self: Sized + Iterator<Item = T>,
{
Cloner::new(self, to_clone)
}
}
impl<I, T, E: Clone> ZipClones<E> for I where I: Iterator<Item = T> {}
pub enum Cloner<I, E>
where
I: Iterator,
{
/// The underlying iterator was exhausted. There is nothing left
Empty,
/// There is at least one element in the underlying iterator. We checked.
More(E, Peekable<I>),
}
impl<I, E> Cloner<I, E>
where
I: Iterator,
{
fn new(it: I, elem: E) -> Self {
let mut it = it.peekable();
if it.peek().is_none() {
Self::Empty
} else {
Self::More(elem, it)
}
}
}
impl<I, T, E> Iterator for Cloner<I, E>
where
E: Clone,
I: Iterator<Item = T>,
{
type Item = (T, E);
fn next(&mut self) -> Option<Self::Item> {
match mem::replace(self, Self::Empty) {
Self::Empty => None,
Self::More(elem, mut it) => {
let next = it.next().expect("We already checked. This is present");
let last = it.peek().is_none();
if last {
// leave the iterator empty after this
Some((next, elem))
} else {
let cloned = elem.clone();
// we checked, and there's more after this
*self = Self::More(elem, it);
Some((next, cloned))
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment