From afab713b4b4598309a5cb748a4d34a56abc058a1 Mon Sep 17 00:00:00 2001 From: Kirill Semyonkin Date: Fri, 7 Mar 2025 20:04:14 +0300 Subject: [PATCH 1/2] Implement IntoIterator for IMap and IArray via self-reference --- Cargo.toml | 1 + src/array.rs | 45 ++++++++++++++++++++-------------------- src/map.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 75 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eacaa6f..dbbff1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ derive = ["implicit-clone-derive"] implicit-clone-derive = { version = "0.1", optional = true, path = "./implicit-clone-derive" } indexmap = { version = "2", optional = true } serde = { version = "1", optional = true } +ouroboros = "0.18" [dev-dependencies] static_assertions = "1" diff --git a/src/array.rs b/src/array.rs index 9813936..e736ee7 100644 --- a/src/array.rs +++ b/src/array.rs @@ -1,5 +1,7 @@ use std::fmt; +use ouroboros::self_referencing; + use super::Rc; use crate::ImplicitClone; @@ -110,24 +112,30 @@ impl From<[T; N]> for IArray { } } -/// An iterator over the cloned elements of an `IArray`. -#[derive(Debug)] -pub struct IArrayIntoIter { +#[self_referencing] +struct IArrayIntoIterInternal { array: IArray, - left: usize, - right: usize, + #[covariant] + #[borrows(array)] + iter: std::slice::Iter<'this, T>, } +/// An iterator over the cloned elements of an `IArray`. +#[allow(missing_debug_implementations)] +pub struct IArrayIntoIter(IArrayIntoIterInternal); + impl IntoIterator for IArray { type Item = T; type IntoIter = IArrayIntoIter; fn into_iter(self) -> ::IntoIter { - IArrayIntoIter { - left: 0, - right: self.len(), - array: self, - } + IArrayIntoIter( + IArrayIntoIterInternalBuilder { + array: self, + iter_builder: |a| a.iter(), + } + .build(), + ) } } @@ -135,22 +143,13 @@ impl Iterator for IArrayIntoIter { type Item = T; fn next(&mut self) -> Option { - if self.left >= self.right { - return None; - } - let item = &self.array[self.left]; - self.left += 1; - Some(item.clone()) + self.0.with_iter_mut(|iter| iter.next().cloned()) } } impl DoubleEndedIterator for IArrayIntoIter { fn next_back(&mut self) -> Option { - if self.left >= self.right { - return None; - } - self.right -= 1; - Some(self.array[self.right].clone()) + self.0.with_iter_mut(|iter| iter.next_back().cloned()) } } @@ -333,7 +332,7 @@ impl IArray { } } -impl<'a, T, U, const N: usize> PartialEq<&'a [U; N]> for IArray +impl PartialEq<&[U; N]> for IArray where T: PartialEq + ImplicitClone, { @@ -369,7 +368,7 @@ where } } -impl<'a, T, U> PartialEq<&'a [U]> for IArray +impl PartialEq<&[U]> for IArray where T: PartialEq + ImplicitClone, { diff --git a/src/map.rs b/src/map.rs index a49b261..c67abcc 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2,6 +2,7 @@ use indexmap::map::Iter as MapIter; use indexmap::map::Keys as MapKeys; use indexmap::map::Values as MapValues; use indexmap::IndexMap as Map; +use ouroboros::self_referencing; use std::borrow::Borrow; use std::fmt; use std::hash::Hash; @@ -308,8 +309,8 @@ impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + } } -impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static> - DoubleEndedIterator for IMapIter<'a, K, V> +impl + DoubleEndedIterator for IMapIter<'_, K, V> { fn next_back(&mut self) -> Option { match self { @@ -319,6 +320,51 @@ impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + } } +#[self_referencing] +struct IMapIntoIterInternal< + K: Eq + Hash + ImplicitClone + 'static, + V: PartialEq + ImplicitClone + 'static, +> { + map: IMap, + #[covariant] + #[borrows(map)] + iter: IMapIter<'this, K, V>, +} + +#[allow(missing_docs, missing_debug_implementations)] +pub struct IMapIntoIter< + K: Eq + Hash + ImplicitClone + 'static, + V: PartialEq + ImplicitClone + 'static, +>(IMapIntoIterInternal); + +impl IntoIterator + for IMap +{ + type Item = (K, V); + type IntoIter = IMapIntoIter; + + fn into_iter(self) -> Self::IntoIter { + IMapIntoIter( + IMapIntoIterInternalBuilder { + map: self, + iter_builder: |a| a.iter(), + } + .build(), + ) + } +} + +impl Iterator + for IMapIntoIter +{ + type Item = (K, V); + + fn next(&mut self) -> Option { + self.0 + .with_iter_mut(|iter| iter.next().map(|(k, v)| (k.clone(), v.clone()))) + } +} + #[allow(missing_docs, missing_debug_implementations)] pub enum IMapKeys<'a, K, V> { Slice(std::slice::Iter<'a, (K, V)>), @@ -338,8 +384,8 @@ impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + } } -impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static> - DoubleEndedIterator for IMapKeys<'a, K, V> +impl + DoubleEndedIterator for IMapKeys<'_, K, V> { fn next_back(&mut self) -> Option { match self { @@ -368,8 +414,8 @@ impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + } } -impl<'a, K: Eq + Hash + ImplicitClone + 'static, V: PartialEq + ImplicitClone + 'static> - DoubleEndedIterator for IMapValues<'a, K, V> +impl + DoubleEndedIterator for IMapValues<'_, K, V> { fn next_back(&mut self) -> Option { match self { From f40122ba73e3356f7104e6aeb05b248dfde34f87 Mon Sep 17 00:00:00 2001 From: Kirill Semyonkin Date: Fri, 7 Mar 2025 20:57:14 +0300 Subject: [PATCH 2/2] Bring over iterator tests --- src/array.rs | 2 +- src/map.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/array.rs b/src/array.rs index e736ee7..5edc594 100644 --- a/src/array.rs +++ b/src/array.rs @@ -489,7 +489,7 @@ mod test_array { } #[test] - fn into_iter() { + fn iterators() { let array = IArray::Static(&[1, 2, 3]); assert_eq!(array.iter().next().unwrap(), &1); assert_eq!(array.into_iter().next().unwrap(), 1); diff --git a/src/map.rs b/src/map.rs index c67abcc..0907a94 100644 --- a/src/map.rs +++ b/src/map.rs @@ -518,4 +518,13 @@ mod test_map { let x: IMap = IMap::Static(&[]); let _out = IMap::from(&x); } + + #[test] + fn iterators() { + let map = IMap::Static(&[(1, 10), (2, 20), (3, 30)]); + assert_eq!(map.iter().next().unwrap(), (&1, &10)); + assert_eq!(map.keys().next().unwrap(), &1); + assert_eq!(map.values().next().unwrap(), &10); + assert_eq!(map.into_iter().next().unwrap(), (1, 10)); + } }