commit ccb0bde71f40061f3e167dde81457893c95b92aa Author: Avril Date: Mon Nov 9 14:58:07 2020 +0000 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..80aca69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +*~ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9883c41 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ad-hoc-iter" +version = "0.1.0" +authors = ["Avril "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/either.rs b/src/either.rs new file mode 100644 index 0000000..65ccee0 --- /dev/null +++ b/src/either.rs @@ -0,0 +1,147 @@ +//! Iterator types that can be 0, 1, or more. +//! +//! This exists to prevent needless heap allocations. + +#![allow(dead_code)] + +/// An iterator that can yield `0,1,2+` items +#[derive(Debug, Clone)] +pub enum MaybeMany +{ + None, + One(T), + Many(U), +} + +impl MaybeMany +{ + /// Try to guess the size. + /// + /// * `None` => `Some(0)` + /// * `One(_)` => `Some(1)` + /// * `Many(_) => `None` + #[inline] pub const fn size_hint(&self) -> Option + { + match self { + Self::None => Some(0), + Self::One(_) => Some(1), + Self::Many(_) => None + } + } + /// Is this an empty instance + #[inline] pub const fn is_none(&self) -> bool + { + if let Self::None = self { + true + } else { + false + } + } + /// Is this a single value instance? + #[inline] pub const fn is_single(&self) -> bool + { + if let Self::One(_) = self { + true + } else { + false + } + } + /// Is this a 2+ value instance? + #[inline] pub const fn is_many(&self) -> bool + { + if let Self::Many(_) = self { + true + } else { + false + } + } + /// Map the single value with this function + #[inline] pub fn map_many(self, fun: F) -> MaybeMany + where F: FnOnce(U) -> A, + A: IntoIterator + { + match self { + Self::One(t) => MaybeMany::One(t), + Self::Many(m) => MaybeMany::Many(fun(m)), + Self::None => MaybeMany::None, + } + } + + /// Map the single value with this function + #[inline] fn map_one(self, fun: F) -> MaybeMany + where F: FnOnce(T) -> T + { + match self { + Self::One(t) => MaybeMany::One(fun(t)), + Self::Many(m) => MaybeMany::Many(m), + Self::None => MaybeMany::None, + } + } + + /// Map both the single and many results + fn map(self, one: Fo, many: Fm) -> MaybeMany + where Fo: FnOnce(T) -> A, + Fm: FnOnce(U) -> B, + B: IntoIterator + { + match self { + Self::One(o) => MaybeMany::One(one(o)), + Self::Many(m) => MaybeMany::Many(many(m)), + Self::None => MaybeMany::None, + } + } + /// Take the value from this instance and replace it with nothing. + #[inline] pub fn take(&mut self) -> Self + { + std::mem::replace(self, Self::None) + } +} + +/// An iterator for `MaybeMany` instances. +#[non_exhaustive] #[derive(Debug, Clone)] +pub enum MaybeManyIter +{ + None, + One(std::iter::Once), + Many(std::iter::Fuse), +} + +impl Iterator for MaybeManyIter +where U: Iterator +{ + type Item = T; + fn next(&mut self) -> Option + { + match self { + Self::None => None, + Self::One(one) => one.next(), + Self::Many(many) => many.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + Self::None => (0, Some(0)), + Self::One(_) => (1, Some(1)), + Self::Many(many) => many.size_hint(), + } + } +} +impl> std::iter::FusedIterator for MaybeManyIter{} +impl> std::iter::ExactSizeIterator for MaybeManyIter +where U: ExactSizeIterator{} + +impl> IntoIterator for MaybeMany +{ + type Item= T; + type IntoIter = MaybeManyIter::IntoIter>; + + fn into_iter(self) -> Self::IntoIter + { + match self { + Self::None => MaybeManyIter::None, + Self::One(one) => MaybeManyIter::One(std::iter::once(one)), + Self::Many(many) => MaybeManyIter::Many(many.into_iter().fuse()) + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8036bde --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,98 @@ +//! Iterator types + +pub mod either; + +/// A bespoke iterator type with an exact size over elements. +/// +/// This has an advantage over using simple inline slices (e.g `[1,2,3]`) as they currently cannot implement `IntoIterator` properly, and will always yield references. +/// This can be a hinderance when you want an ad-hoc iterator of non-`Copy` items. +/// The iterator types created from this macro consume the values. +/// +/// # Example +/// ``` +/// # use ad_hoc_iter::iter; +/// let sum: i32 = iter![1, 2].chain(3..=4).chain(iter![5, 6]).sum(); +/// assert_eq!(sum, 21); +/// ``` +#[macro_export] macro_rules! iter { + (@) => (0usize); + (@ $x:tt $($xs:tt)* ) => (1usize + $crate::iter!(@ $($xs)*)); + + ($($value:expr),*) => { + { + use ::std::mem::MaybeUninit; + use ::std::ops::Drop; + struct Arr([MaybeUninit; $crate::iter!(@ $($value)*)], usize); + impl Arr + { + const LEN: usize = $crate::iter!(@ $($value)*); + } + impl Iterator for Arr + { + type Item = T; + fn next(&mut self) -> Option + { + if self.1 >= self.0.len() { + None + } else { + //take one + let one = unsafe { + ::std::mem::replace(&mut self.0[self.1], MaybeUninit::uninit()).assume_init() + }; + self.1+=1; + Some(one) + } + } + + #[inline] fn size_hint(&self) -> (usize, Option) + { + (Self::LEN, Some(Self::LEN)) + } + } + impl ::std::iter::FusedIterator for Arr{} + impl ::std::iter::ExactSizeIterator for Arr{} + + impl Drop for Arr + { + fn drop(&mut self) { + if ::std::mem::needs_drop::() { + for idx in self.1..self.0.len() { + unsafe { + ::std::mem::replace(&mut self.0[idx], MaybeUninit::uninit()).assume_init(); + } + } + } + } + } + + Arr([$(MaybeUninit::new($value)),*], 0) + } + } +} + +#[cfg(test)] +mod tests +{ + #[test] + fn iter_over() + { + const EXPECT: usize = 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1; + let iter = iter![10,9,8,7,6,5,4,3,2,1]; + + assert_eq!(iter.len(), 10); + assert_eq!(iter.sum::(), EXPECT); + + } + + #[test] + fn non_copy() + { + macro_rules! string { + ($val:literal) => (String::from($val)); + } + + const EXPECT: &str = "Hello world!"; + let whole: String = iter![string!("Hell"), string!("o "), string!("world"), string!("!")].collect(); + assert_eq!(EXPECT, &whole[..]); + } +}