From 9cf85bd7fe9d0135bc3ed369f3ffc5760b5e76c7 Mon Sep 17 00:00:00 2001 From: Avril Date: Mon, 9 Nov 2020 15:48:30 +0000 Subject: [PATCH] added README --- Cargo.toml | 9 ++- README.md | 49 +++++++++++++ src/lib.rs | 134 ++++++++++++++++++++++++++++++++++-- src/{either.rs => maybe.rs} | 0 4 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 README.md rename src/{either.rs => maybe.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 9883c41..03591d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,16 @@ [package] name = "ad-hoc-iter" version = "0.1.0" +description = "Ad-hoc exact size owning iterator macro and other optional utils" +keywords = ["iterator", "macro", "iter"] authors = ["Avril "] edition = "2018" +license = "mit" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["maybe-many"] + +# Enable the `maybe` module +maybe-many = [] [dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..827071f --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Ad-hoc owning iterator types + +This crate defines the macro `iter!` which produces ad-hoc iterator types that own their values and have compile-time known exact sizes. + +# Usage +This macro can be used exactly like `vec!`, except it produces an `impl Iterator` that does not allocate. +This `impl Iterator` yields the values directly, **not references** to the values. + +This can be useful for when you want a ad-hoc iterator of a non-copy type, as sized slices and arrays currently do not implement `IntoIterator` in a way that moves their values, instead they yield references, causing the need for cloning. + +The `iter!` macro's iterator types move their values on calls to `next()` instead of returning references, and drop the non-consumed values when the iterator is dropped itself. + +## Example + +Concatenating a 'slice' of `String` without cloning. +``` rust +let whole: String = iter![String::from("Hell"), + String::from("o "), + String::from("world"), + String::from("!")] + .collect(); +assert_eq!("Hello world!", &whole[..]); +``` + +## Functions +The iterator types also have a few associated functions. + +### The length of the whole iterator +```rust +pub const fn len(&self) -> usize +``` + +### The rest of the iterator that has not been consumed. +```rust +pub fn rest(&self) -> &[T] +``` + +### The whole array. +All values that have not been consumed are initialised, values that have been consumed are uninitialised. +```rust +pub fn array(&self) -> &[MaybeUninit; Self::LEN] +``` + +### How many items have since been consumed. +```rust +pub const fn consumed(&self) -> usize +``` +# License +MIT diff --git a/src/lib.rs b/src/lib.rs index 8036bde..6bc3f3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,16 @@ -//! Iterator types +//! Ad-hoc exact size owning iterator macro and other optional utils +//! +//! The macro can be used exactly as `vec!`. -pub mod either; +#[cfg(feature="maybe-many")] pub mod maybe; /// 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. +/// It has the advantage over `vec![].into_iter()` as there are no heap allocations needed and size of the iterator is known at compile time. /// /// # Example /// ``` @@ -14,9 +18,56 @@ pub mod either; /// let sum: i32 = iter![1, 2].chain(3..=4).chain(iter![5, 6]).sum(); /// assert_eq!(sum, 21); /// ``` +/// # Functions +/// The iterators returned from this method have these associated functions: +/// +/// ## The length of the whole iterator +/// ``` +/// pub const fn len(&self) -> usize +/// ``` +/// +/// ## The rest of the iterator that has not been consumed. +/// ``` +/// pub fn rest(&self) -> &[T] +/// ``` +/// +/// ## The whole array. +/// All values that have not been consumed are initialised, values that have been consumed are uninitialised. +/// ``` +/// pub fn array(&self) -> &[MaybeUninit; Self::LEN] +/// ``` +/// +/// ## How many items have since been consumed. +/// ``` +/// pub const fn consumed(&self) -> usize +/// ``` #[macro_export] macro_rules! iter { (@) => (0usize); (@ $x:tt $($xs:tt)* ) => (1usize + $crate::iter!(@ $($xs)*)); + + () => { + { + #[derive(Debug)] + struct Empty; + impl Iterator for Empty + { + type Item = std::convert::Infallible; + fn next(&mut self) -> Option + { + None + } + + fn size_hint(&self) -> (usize, Option) + { + (0, Some(0)) + } + } + impl ::std::iter::FusedIterator for Empty{} + impl ::std::iter::ExactSizeIterator for Empty{} + + Empty + } + }; ($($value:expr),*) => { { @@ -25,7 +76,53 @@ pub mod either; struct Arr([MaybeUninit; $crate::iter!(@ $($value)*)], usize); impl Arr { + #![allow(dead_code)] + /// The length of the whole iterator const LEN: usize = $crate::iter!(@ $($value)*); + + /// The length of the whole iterator + // This exists as an associated function because this type is opaque. + #[inline] pub const fn len(&self) -> usize + { + Self::LEN + } + + /// Consume this iterator into the backing buffer. + /// + /// # Safety + /// Non-consumed items are safe to `assume_init`. However, items that have already been consumed are uninitialised. + fn into_inner(self) -> [MaybeUninit; $crate::iter!(@ $($value)*)] + { + //XXX: We will have to do something really unsafe for this to work on stable... + todo!() + } + + /// The rest of the iterator that has not been consumed. + /// + // # Safety + // All values in this slice are initialised. + #[inline] pub fn rest(&self) -> &[T] + { + let slice = &self.0[self.1..]; + //std::mem::MaybeUninit::slice_get_ref(&self.0[self.1..]) //nightly only... + + unsafe { &*(slice as *const [std::mem::MaybeUninit] as *const [T]) } + } + + /// The whole array. + /// + /// # Safety + /// All values that have not been consumed are initialised, values that have been consumed are uninitialised. + #[inline] pub fn array(&self) -> &[MaybeUninit; $crate::iter!(@ $($value)*)] + { + &self.0 + } + + /// How many items have since been consumed. + pub const fn consumed(&self) -> usize + { + self.1 + } } impl Iterator for Arr { @@ -84,15 +181,42 @@ mod tests } + + macro_rules! string { + ($val:literal) => (String::from($val)); + } #[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[..]); } + + #[test] + fn empty() + { + let empty: Vec<_> = iter![].collect(); + assert_eq!(empty.len(), 0); + } + + #[test] + fn assoc() + { + let mut iter = iter![1,2,3,4]; + + assert_eq!(iter.len(), 4); + iter.next(); + assert_eq!(iter.consumed(), 1); + assert_eq!(iter.rest(), &[2,3,4]); + + + let mut iter = iter![string!("Hell"), string!("o "), string!("world"), string!("!")]; + + iter.next(); + iter.next(); + + assert_eq!(iter.rest().iter().map(|x| x.as_str()).collect::>().as_slice(), &["world", "!"]); + } } diff --git a/src/either.rs b/src/maybe.rs similarity index 100% rename from src/either.rs rename to src/maybe.rs