From ef91d9a20623dcb217448c1695c6821e40e72537 Mon Sep 17 00:00:00 2001 From: Karan Jain Date: Tue, 29 Jul 2025 17:43:01 -0700 Subject: [PATCH 1/2] remove methods --- src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 07465bb..c1c004c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ use serde::{ Deserialize, Serialize, }; +use core::fmt; use core::iter; use core::mem; use core::{cmp::Ordering, num::NonZeroUsize}; @@ -215,6 +216,22 @@ impl ExactSizeIterator for Iter<'_, T> { impl core::iter::FusedIterator for Iter<'_, T> {} +pub enum RemoveError { + LengthOne, + IndexOutOfBounds { index: usize, len: usize }, +} + +impl fmt::Debug for RemoveError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::LengthOne => write!(f, "Cannot remove from NonEmpty of length 1"), + Self::IndexOutOfBounds { index, len } => { + write!(f, "removal index ({index}) should be < len ({len})") + } + } + } +} + impl NonEmpty { /// Alias for [`NonEmpty::singleton`]. pub const fn new(e: T) -> Self { @@ -338,6 +355,42 @@ impl NonEmpty { } } + pub fn remove(&mut self, index: usize) -> T { + self.checked_remove(index).unwrap() + } + + pub fn checked_remove(&mut self, index: usize) -> Result { + if self.tail.is_empty() { + return Err(RemoveError::LengthOne); + } + let len = self.len(); + if index >= len { + return Err(RemoveError::IndexOutOfBounds { index, len }); + } + match index.checked_sub(1) { + None => Ok(mem::replace(&mut self.head, self.tail.remove(0))), + Some(tail_index) => Ok(self.tail.remove(tail_index)), + } + } + + pub fn swap_remove(&mut self, index: usize) -> T { + self.checked_swap_remove(index).unwrap() + } + + pub fn checked_swap_remove(&mut self, index: usize) -> Result { + if self.tail.is_empty() { + return Err(RemoveError::LengthOne); + } + let len = self.len(); + if index >= len { + return Err(RemoveError::IndexOutOfBounds { index, len }); + } + match index.checked_sub(1) { + None => Ok(mem::replace(&mut self.head, self.tail.pop().unwrap())), + Some(tail_index) => Ok(self.tail.swap_remove(tail_index)), + } + } + /// Get the length of the list. pub fn len(&self) -> usize { self.tail.len() + 1 From 9431d745ebcfa65e7a6969c46496605df637219e Mon Sep 17 00:00:00 2001 From: Karan Jain Date: Tue, 29 Jul 2025 17:53:57 -0700 Subject: [PATCH 2/2] add docstrings and doctests --- src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index c1c004c..1c66009 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -355,10 +355,35 @@ impl NonEmpty { } } + /// Removes an element from the vector, shifting all elements after it to the left. + /// + /// # Panics + /// + /// Panics if the index is out of bounds or the tail vector is empty. pub fn remove(&mut self, index: usize) -> T { self.checked_remove(index).unwrap() } + /// Checks if the element at a certain index can be removed from the vector, then removes it, + /// shifting all elements after it to the left. + /// + /// # Errors + /// + /// Returns `Err` if the index is out of bounds or the tail vector is empty. + /// + /// # Examples + /// + /// ``` + /// use nonempty::NonEmpty; + /// + /// let mut non_empty = nonempty![1, 2, 3]; + /// assert_eq!(non_empty.checked_remove(1), Ok(2)); + /// assert_eq!(non_empty, nonempty![1, 3]); + /// assert_eq!(non_empty.checked_remove(3), Err(RemoveError::IndexOutOfBounds { index: 3, len: 2 })); + /// assert_eq!(non_empty.checked_remove(0), Ok(1)); + /// assert_eq!(non_empty, nonempty![3]); + /// assert_eq!(non_empty.checked_remove(0), Err(RemoveError::LengthOne)); + /// ``` pub fn checked_remove(&mut self, index: usize) -> Result { if self.tail.is_empty() { return Err(RemoveError::LengthOne); @@ -373,10 +398,33 @@ impl NonEmpty { } } + /// Removes an element from the vector, replacing it with the last element. + /// + /// # Panics + /// + /// Panics if the index is out of bounds or the tail vector is empty. pub fn swap_remove(&mut self, index: usize) -> T { self.checked_swap_remove(index).unwrap() } + /// Checks if the element at a certain index can be removed from the vector, then removes it, replacing it with the last element. + /// + /// # Errors + /// + /// Returns `Err` if the index is out of bounds or the tail vector is empty. + /// + /// # Examples + /// + /// ``` + /// use nonempty::NonEmpty; + /// + /// let mut non_empty = nonempty![1, 2, 3, 4]; + /// assert_eq!(non_empty.checked_swap_remove(1), Ok(2)); + /// assert_eq!(non_empty, nonempty![1, 4, 3]); + /// assert_eq!(non_empty.checked_swap_remove(3), Err(RemoveError::IndexOutOfBounds { index: 3, len: 3 })); + /// assert_eq!(non_empty.checked_swap_remove(0), Ok(1)); + /// assert_eq!(non_empty, nonempty![3, 4]); + /// ``` pub fn checked_swap_remove(&mut self, index: usize) -> Result { if self.tail.is_empty() { return Err(RemoveError::LengthOne);