#![no_std] //! provides [`collect_array`](CollectArray::collect_array) and [`try_from_fn`]. //! allowing easy vec-free collection to an array. //! //! ``` //! # /* //! use collar::*; //! let Some([ty, path, http]) = request.split(' ').collect_array_checked() else { //! return; //! }; //! # */ //! ``` use core::{ mem::{ManuallyDrop as MD, MaybeUninit as MU, forget}, ptr::drop_in_place, }; use error::Error; pub use error::Error as CollectorError; mod error; mod maybe; use maybe::Maybe; /// Collect to an array. pub trait CollectArray: Iterator + Sized { /// Lets you collect an iterator into a fixed length array with no vec allocation. /// Handle remainder as you wish. Does not consume the iterator. /// /// # Panics /// /// when [next](Iterator::next)() is [None] before the array is filled. /// for a non panicking alternative, see [`collect_array_checked`](CollectArray::collect_array_checked). /// /// ``` /// use collar::*; /// let array = (0usize..).map(|x| x * 2).collect_array(); /// // indexes are: 0 1 2 3 4 5 6 7 /// assert_eq!(array, [0, 2, 4, 6, 8, 10, 12, 14]); /// ``` fn collect_array(&mut self) -> [Self::Item; N] { self.collect_array_checked() .unwrap_or_else(|x| panic!("couldnt fill buffer of length {N} only had {x} elements")) } /// Non panicking version of [`collect_array`](CollectArray::collect_array). /// /// Lets you collect an iterator into a fixed length array with no vec allocation, with no panics. /// If the iterator returns [`None`] at any point, returns [Err]\(elements filed\). /// /// If you wish to simply populate the array with [`None`] if the iterator returns [`None`], use [`items`](CollectArray::items). /// /// ``` /// use collar::*; /// let array: Result<[u8; 10], usize> = std::iter::repeat(5).take(3).collect_array_checked(); /// // does not fill array -> produces `Err`, with number of elements filled. /// assert_eq!(array, Err(3)); /// ``` fn collect_array_checked(&mut self) -> Result<[Self::Item; N], usize> { try_from_fn(|elem| self.next().ok_or(elem)) } /// Creates an array [T; N] where each fallible (i.e [`Option`] or [`Result`]) element is begotten from [`next`](Iterator::next). /// Unlike [`collect_array`](CollectArray::collect_array), where the element creation can't fail, this version will return an error if any element creation was unsuccessful (returned [`Err`] or [`None`]). /// In the case where the iterator ran out of elements, this returns a [`CollectorError`] containing the count. /// /// The return type of this function depends on the [`Item`](Iterator::Item) of this [`Iterator`]. /// If you return `Result` from the closure, you'll get a `Result<[T; N], CollectorError>`. /// If you return `Option` from the closure, you'll get an `Result<[T; N], CollectorError<()>>`. /// ``` /// use collar::CollectArray; /// let array: Result<[i8; 200], _> = (0..).map(|x| x.try_into()).try_collect_array(); /// assert_eq!(array.unwrap_err().at, 128); /// /// // note the ok(); the try trait is still unstable. (so this is a Result<_, ()>::ok) /// let array: Option<[_; 4]> = (0usize..).map(|i| i.checked_add(100)).try_collect_array().ok(); /// assert_eq!(array, Some([100, 101, 102, 103])); /// /// let array: Option<[_; 4]> = (0usize..).map(|i| i.checked_sub(100)).try_collect_array().ok(); /// assert_eq!(array, None); /// ``` fn try_collect_array( &mut self, ) -> Result<[::Unwrap; N], Error::Or>> where Self::Item: Maybe, { try_from_fn(|elem| { self.next() // no error, ran out .ok_or(None) // some error, flattened .and_then(|x| x.asr().map_err(Some)) .map_err(|x| Error { error: x, at: elem }) }) } /// This function fills an array with this iterators elements. /// It will always return (unless the iterator panics). /// ``` /// use collar::*; /// assert_eq!( /// (0..).items::<5>(), /// (0..).map(Some).collect_array::<5>(), /// ) /// ``` fn items(&mut self) -> [Option; N] { from_fn(|_| self.next()) } } impl CollectArray for I {} struct OnDrop { f: MD, } impl OnDrop { fn guard(x: F) -> Self { Self { f: MD::new(x) } } } impl Drop for OnDrop { fn drop(&mut self) { // SAFETY: `Drop::drop` is only called once. unsafe { MD::take(&mut self.f)() } } } const unsafe fn transmute_unchecked(value: T) -> U { const { assert!(size_of::() == size_of::()) } #[repr(C)] union Transmute { t: MD, u: MD, } unsafe { MD::into_inner(Transmute { t: MD::new(value) }.u) } } /// [`std::array::try_from_fn`] on stable. /// /// Creates an array `[T; N]` where each fallible array element `T` is returned by the `cb` call. /// Unlike [`from_fn`], where the element creation can't fail, this version will return an error /// if any element creation was unsuccessful. /// /// The return type of this function depends on the return type of the closure. /// If you return `Result` from the closure, you'll get a `Result<[T; N], E>`. /// If you return `Option` from the closure, you'll get an `Result<[T; N], ()>`. /// /// # Arguments /// /// * `cb`: Callback where the passed argument is the current array index. /// /// # Example /// /// ```rust /// let array: Result<[u8; 5], _> = collar::try_from_fn(|i| i.try_into()); /// assert_eq!(array, Ok([0, 1, 2, 3, 4])); /// /// let array: Result<[i8; 200], _> = collar::try_from_fn(|i| i.try_into()); /// assert!(array.is_err()); /// /// let array: Option<[_; 4]> = collar::try_from_fn(|i| i.checked_add(100)).ok(); /// assert_eq!(array, Some([100, 101, 102, 103])); /// /// let array: Option<[_; 4]> = collar::try_from_fn(|i| i.checked_sub(100)).ok(); /// assert_eq!(array, None); /// ``` pub fn try_from_fn( mut x: impl FnMut(usize) -> R, ) -> Result<[R::Unwrap; N], R::Or> { let mut out = [const { MU::uninit() }; N]; // initialize each element of `out` for elem in 0..N { let guard = OnDrop::guard(|| unsafe { let p = &raw mut out[..elem] as *mut [R::Unwrap]; let guard = OnDrop::guard(|| drop_in_place(p)); drop_in_place(p); // dont drop! (again) forget(guard); }); let e = x(elem).asr()?; // dont drop! forget(guard); out[elem] = MU::new(e); } // SAFETY: each element has been initialized Ok(unsafe { transmute_unchecked(out) }) } #[doc(no_inline)] pub use core::array::from_fn;