diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index ff996ba93cd7f..27a41369d4e5e 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -399,6 +399,21 @@ impl RawVec { // SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) } } + + /// Shrinks the buffer down to the specified capacity. If the given amount + /// is 0, actually completely deallocates. + /// + /// # Errors + /// + /// This function returns an error if the allocator cannot shrink the allocation. + /// + /// # Panics + /// + /// Panics if the given amount is *larger* than the current capacity. + #[inline] + pub(crate) fn try_shrink_to_fit(&mut self, cap: usize) -> Result<(), TryReserveError> { + unsafe { self.inner.try_shrink_to_fit(cap, T::LAYOUT) } + } } unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec { @@ -731,6 +746,20 @@ impl RawVecInner { } } + /// # Safety + /// + /// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to + /// initially construct `self` + /// - `elem_layout`'s size must be a multiple of its alignment + /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` + unsafe fn try_shrink_to_fit( + &mut self, + cap: usize, + elem_layout: Layout, + ) -> Result<(), TryReserveError> { + unsafe { self.shrink(cap, elem_layout) } + } + #[inline] const fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool { additional > self.capacity(elem_layout.size()).wrapping_sub(len) @@ -778,7 +807,6 @@ impl RawVecInner { /// initially construct `self` /// - `elem_layout`'s size must be a multiple of its alignment /// - `cap` must be less than or equal to `self.capacity(elem_layout.size())` - #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> { assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity"); @@ -796,7 +824,6 @@ impl RawVecInner { /// /// # Safety /// `cap <= self.capacity()` - #[cfg(not(no_global_oom_handling))] unsafe fn shrink_unchecked( &mut self, cap: usize, diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 6cbe89d9da4f2..a196489bfb341 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -75,8 +75,6 @@ #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -#[cfg(not(no_global_oom_handling))] -use core::cmp; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] @@ -88,7 +86,7 @@ use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, Tr use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; -use core::{fmt, hint, intrinsics, ub_checks}; +use core::{cmp, fmt, hint, intrinsics, ub_checks}; #[stable(feature = "extract_if", since = "1.87.0")] pub use self::extract_if::ExtractIf; @@ -1613,6 +1611,73 @@ impl Vec { } } + /// Tries to shrink the capacity of the vector as much as possible + /// + /// The behavior of this method depends on the allocator, which may either shrink the vector + /// in-place or reallocate. The resulting vector might still have some excess capacity, just as + /// is the case for [`with_capacity`]. See [`Allocator::shrink`] for more details. + /// + /// [`with_capacity`]: Vec::with_capacity + /// + /// # Errors + /// + /// This function returns an error if the allocator fails to shrink the allocation, + /// the vector thereafter is still safe to use, the capacity remains unchanged + /// however. See [`Allocator::shrink`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_fallible_shrink)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// assert!(vec.capacity() >= 10); + /// vec.try_shrink_to_fit().expect("why is the test harness failing to shrink to 12 bytes"); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "vec_fallible_shrink", issue = "152350")] + #[inline] + pub fn try_shrink_to_fit(&mut self) -> Result<(), TryReserveError> { + if self.capacity() > self.len { self.buf.try_shrink_to_fit(self.len) } else { Ok(()) } + } + + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// If the current capacity is less than the lower limit, this is a no-op. + /// + /// # Errors + /// + /// This function returns an error if the allocator fails to shrink the allocation, + /// the vector thereafter is still safe to use, the capacity remains unchanged + /// however. See [`Allocator::shrink`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_fallible_shrink)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// assert!(vec.capacity() >= 10); + /// vec.try_shrink_to(4).expect("why is the test harness failing to shrink to 12 bytes"); + /// assert!(vec.capacity() >= 4); + /// vec.try_shrink_to(0).expect("this is a no-op and thus the allocator isn't involved."); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "vec_fallible_shrink", issue = "152350")] + #[inline] + pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), TryReserveError> { + if self.capacity() > min_capacity { + self.buf.try_shrink_to_fit(cmp::max(self.len, min_capacity)) + } else { + Ok(()) + } + } + /// Converts the vector into [`Box<[T]>`][owned slice]. /// /// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`].