about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-09 21:02:33 +0000
committerbors <bors@rust-lang.org>2022-10-09 21:02:33 +0000
commit1a7c203e7f89f92598a793c6f6406735c024e7ee (patch)
tree092e5362232165047754ef24ff91a0a242790bbf
parent81f391930301afbc121b7c468138069daa354bf8 (diff)
parentbb74f97445f04d68e42e7e308a109f78a2d2b0f7 (diff)
downloadrust-1a7c203e7f89f92598a793c6f6406735c024e7ee.tar.gz
rust-1a7c203e7f89f92598a793c6f6406735c024e7ee.zip
Auto merge of #89123 - the8472:push_in_capacity, r=amanieu
add Vec::push_within_capacity - fallible, does not allocate

This method can serve several purposes. It

* is fallible
* guarantees that items in Vec aren't moved
* allows loops that do `reserve` and `push` separately to avoid pulling in the allocation machinery a second time in the `push` part which should make things easier on the optimizer
* eases the path towards `ArrayVec` a bit since - compared to `push()` - there are fewer questions around how it should be implemented

I haven't named it `try_push` because that should probably occupy a middle ground that will still try to reserve and only return an error in the unlikely OOM case.

resolves #84649
-rw-r--r--library/alloc/src/vec/mod.rs45
1 files changed, 45 insertions, 0 deletions
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 0332047e6b6..b2bb7a5b2e6 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1839,6 +1839,51 @@ impl<T, A: Allocator> Vec<T, A> {
         }
     }
 
+    /// Appends an element if there is sufficient spare capacity, otherwise an error is returned
+    /// with the element.
+    ///
+    /// Unlike [`push`] this method will not reallocate when there's insufficient capacity.
+    /// The caller should use [`reserve`] or [`try_reserve`] to ensure that there is enough capacity.
+    ///
+    /// [`push`]: Vec::push
+    /// [`reserve`]: Vec::reserve
+    /// [`try_reserve`]: Vec::try_reserve
+    ///
+    /// # Examples
+    ///
+    /// A manual, panic-free alternative to [`FromIterator`]:
+    ///
+    /// ```
+    /// #![feature(vec_push_within_capacity)]
+    ///
+    /// use std::collections::TryReserveError;
+    /// fn from_iter_fallible<T>(iter: impl Iterator<Item=T>) -> Result<Vec<T>, TryReserveError> {
+    ///     let mut vec = Vec::new();
+    ///     for value in iter {
+    ///         if let Err(value) = vec.push_within_capacity(value) {
+    ///             vec.try_reserve(1)?;
+    ///             // this cannot fail, the previous line either returned or added at least 1 free slot
+    ///             let _ = vec.push_within_capacity(value);
+    ///         }
+    ///     }
+    ///     Ok(vec)
+    /// }
+    /// assert_eq!(from_iter_fallible(0..100), Ok(Vec::from_iter(0..100)));
+    /// ```
+    #[inline]
+    #[unstable(feature = "vec_push_within_capacity", issue = "100486")]
+    pub fn push_within_capacity(&mut self, value: T) -> Result<(), T> {
+        if self.len == self.buf.capacity() {
+            return Err(value);
+        }
+        unsafe {
+            let end = self.as_mut_ptr().add(self.len);
+            ptr::write(end, value);
+            self.len += 1;
+        }
+        Ok(())
+    }
+
     /// Removes the last element from a vector and returns it, or [`None`] if it
     /// is empty.
     ///