diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d65933181..0585240b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - Added `from_bytes_truncating_at_nul` to `CString` +- Added `remove_insert` to `Vec` ## [v0.9.2] 2025-11-12 diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 448b22db34..18850a4c57 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -960,6 +960,46 @@ impl + ?Sized> VecInner { value } + /// This is the same as calling [`Vec::remove`] with `remove_index` + /// followed by calling [`Vec::insert`] with `insert_index` and `element`. + /// + /// The returned value is the removed element. + /// + /// This is more efficient than removing then inserting since it only shifts + /// `remove_index.abs_diff(insert_index)` values. + /// + /// [`remove`]: Vec::remove + /// [`insert`]: Vec::insert + /// + /// # Panics + /// + /// Panics if `remove_index` or `insert_index` are out of bounds. + /// + /// # Examples + /// + /// ``` + /// use heapless::Vec; + /// + /// let mut v: Vec<_, 8> = Vec::from_slice(&[0, 1, 2, 3]).unwrap(); + /// assert_eq!(v.remove_insert(1, 2, 4), 1); + /// // only one element (2) is shifted back + /// assert_eq!(v, [0, 2, 4, 3]); + /// ``` + pub fn remove_insert(&mut self, remove_index: usize, insert_index: usize, element: T) -> T { + let length = self.len(); + + assert!(remove_index < length); + assert!(insert_index < length); + + match remove_index.cmp(&insert_index) { + Ordering::Equal => (), + Ordering::Less => self[remove_index..=insert_index].rotate_left(1), + Ordering::Greater => self[insert_index..=remove_index].rotate_right(1), + } + + mem::replace(&mut self[insert_index], element) + } + /// Returns true if the vec is full pub fn is_full(&self) -> bool { self.len() == self.capacity() @@ -2119,6 +2159,92 @@ mod tests { assert_eq!(v.len(), 0); } + #[test] + fn remove_insert() { + let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut v: Vec = Vec::from_array(arr); + let mut v2: Vec = Vec::from_array(arr); + + // insert_index == remove_index + let n = v.remove_insert(2, 2, 10); + assert_eq!(n, 2); + assert_eq!(v, [0, 1, 10, 3, 4, 5, 6, 7, 8, 9]); + + let n2 = v2.remove(2); + v2.insert(2, 10).unwrap(); + assert_eq!(n, n2); + assert_eq!(v, v2); + + // reset + v.copy_from_slice(&arr); + v2.copy_from_slice(&arr); + + // insert_index > remove_index + let n = v.remove_insert(3, 5, 10); + assert_eq!(n, 3); + assert_eq!(v, [0, 1, 2, 4, 5, 10, 6, 7, 8, 9]); + + let n2 = v2.remove(3); + v2.insert(5, 10).unwrap(); + assert_eq!(n, n2); + assert_eq!(v, v2); + + v.copy_from_slice(&arr); + v2.copy_from_slice(&arr); + + // insert_index < remove_index + let n = v.remove_insert(5, 3, 10); + assert_eq!(n, 5); + assert_eq!(v, [0, 1, 2, 10, 3, 4, 6, 7, 8, 9]); + + let n2 = v2.remove(5); + v2.insert(3, 10).unwrap(); + + assert_eq!(n, n2); + assert_eq!(v, v2); + + // at boundaries + + v.copy_from_slice(&arr); + v2.copy_from_slice(&arr); + + let n = v.remove_insert(0, 9, 10); + assert_eq!(n, 0); + assert_eq!(v, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + let n2 = v2.remove(0); + v2.insert(9, 10).unwrap(); + assert_eq!(n, n2); + assert_eq!(v, v2); + + v.copy_from_slice(&arr); + v2.copy_from_slice(&arr); + + let n = v.remove_insert(9, 0, 10); + assert_eq!(n, 9); + assert_eq!(v, [10, 0, 1, 2, 3, 4, 5, 6, 7, 8]); + + let n2 = v2.remove(9); + v2.insert(0, 10).unwrap(); + assert_eq!(n, n2); + assert_eq!(v, v2); + } + + #[test] + #[should_panic] + fn remove_insert_out_of_bounds() { + let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let mut v: Vec = Vec::from_array(arr); + let _ = v.remove_insert(0, 10, 10); + } + + #[test] + #[should_panic] + fn remove_insert_empty() { + let mut v: Vec = Vec::from_array([]); + let _ = v.remove_insert(0, 0, 10); + } + #[test] fn resize_size_limit() { let mut v: Vec = Vec::new();