Skip to content

Commit 51b5459

Browse files
authored
Rollup merge of rust-lang#152366 - BitSyndicate1:vec-fallible-shrink, r=Mark-Simulacrum
Add try_shrink_to and try_shrink_to_fit to Vec Adds the functions `try_shrink_to` and `try_shrink_to_fit` to Vec to allow shrinking in environments without global OOM handling. The implementation differs from the tracking issue as the fallible methods return a `TryReserveError` instead of an `AllocError` as `AllocError` is unstable and does not contain layout information. Tracking: - rust-lang#152350
2 parents 565d1bb + 1f076d2 commit 51b5459

File tree

2 files changed

+97
-5
lines changed

2 files changed

+97
-5
lines changed

library/alloc/src/raw_vec/mod.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,21 @@ impl<T, A: Allocator> RawVec<T, A> {
399399
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
400400
unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) }
401401
}
402+
403+
/// Shrinks the buffer down to the specified capacity. If the given amount
404+
/// is 0, actually completely deallocates.
405+
///
406+
/// # Errors
407+
///
408+
/// This function returns an error if the allocator cannot shrink the allocation.
409+
///
410+
/// # Panics
411+
///
412+
/// Panics if the given amount is *larger* than the current capacity.
413+
#[inline]
414+
pub(crate) fn try_shrink_to_fit(&mut self, cap: usize) -> Result<(), TryReserveError> {
415+
unsafe { self.inner.try_shrink_to_fit(cap, T::LAYOUT) }
416+
}
402417
}
403418

404419
unsafe impl<#[may_dangle] T, A: Allocator> Drop for RawVec<T, A> {
@@ -731,6 +746,20 @@ impl<A: Allocator> RawVecInner<A> {
731746
}
732747
}
733748

749+
/// # Safety
750+
///
751+
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
752+
/// initially construct `self`
753+
/// - `elem_layout`'s size must be a multiple of its alignment
754+
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
755+
unsafe fn try_shrink_to_fit(
756+
&mut self,
757+
cap: usize,
758+
elem_layout: Layout,
759+
) -> Result<(), TryReserveError> {
760+
unsafe { self.shrink(cap, elem_layout) }
761+
}
762+
734763
#[inline]
735764
const fn needs_to_grow(&self, len: usize, additional: usize, elem_layout: Layout) -> bool {
736765
additional > self.capacity(elem_layout.size()).wrapping_sub(len)
@@ -778,7 +807,6 @@ impl<A: Allocator> RawVecInner<A> {
778807
/// initially construct `self`
779808
/// - `elem_layout`'s size must be a multiple of its alignment
780809
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
781-
#[cfg(not(no_global_oom_handling))]
782810
#[inline]
783811
unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
784812
assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity");
@@ -796,7 +824,6 @@ impl<A: Allocator> RawVecInner<A> {
796824
///
797825
/// # Safety
798826
/// `cap <= self.capacity()`
799-
#[cfg(not(no_global_oom_handling))]
800827
unsafe fn shrink_unchecked(
801828
&mut self,
802829
cap: usize,

library/alloc/src/vec/mod.rs

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@
7575

7676
#[cfg(not(no_global_oom_handling))]
7777
use core::clone::TrivialClone;
78-
#[cfg(not(no_global_oom_handling))]
79-
use core::cmp;
8078
use core::cmp::Ordering;
8179
use core::hash::{Hash, Hasher};
8280
#[cfg(not(no_global_oom_handling))]
@@ -88,7 +86,7 @@ use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, Tr
8886
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
8987
use core::ptr::{self, NonNull};
9088
use core::slice::{self, SliceIndex};
91-
use core::{fmt, hint, intrinsics, ub_checks};
89+
use core::{cmp, fmt, hint, intrinsics, ub_checks};
9290

9391
#[stable(feature = "extract_if", since = "1.87.0")]
9492
pub use self::extract_if::ExtractIf;
@@ -1613,6 +1611,73 @@ impl<T, A: Allocator> Vec<T, A> {
16131611
}
16141612
}
16151613

1614+
/// Tries to shrink the capacity of the vector as much as possible
1615+
///
1616+
/// The behavior of this method depends on the allocator, which may either shrink the vector
1617+
/// in-place or reallocate. The resulting vector might still have some excess capacity, just as
1618+
/// is the case for [`with_capacity`]. See [`Allocator::shrink`] for more details.
1619+
///
1620+
/// [`with_capacity`]: Vec::with_capacity
1621+
///
1622+
/// # Errors
1623+
///
1624+
/// This function returns an error if the allocator fails to shrink the allocation,
1625+
/// the vector thereafter is still safe to use, the capacity remains unchanged
1626+
/// however. See [`Allocator::shrink`].
1627+
///
1628+
/// # Examples
1629+
///
1630+
/// ```
1631+
/// #![feature(vec_fallible_shrink)]
1632+
///
1633+
/// let mut vec = Vec::with_capacity(10);
1634+
/// vec.extend([1, 2, 3]);
1635+
/// assert!(vec.capacity() >= 10);
1636+
/// vec.try_shrink_to_fit().expect("why is the test harness failing to shrink to 12 bytes");
1637+
/// assert!(vec.capacity() >= 3);
1638+
/// ```
1639+
#[unstable(feature = "vec_fallible_shrink", issue = "152350")]
1640+
#[inline]
1641+
pub fn try_shrink_to_fit(&mut self) -> Result<(), TryReserveError> {
1642+
if self.capacity() > self.len { self.buf.try_shrink_to_fit(self.len) } else { Ok(()) }
1643+
}
1644+
1645+
/// Shrinks the capacity of the vector with a lower bound.
1646+
///
1647+
/// The capacity will remain at least as large as both the length
1648+
/// and the supplied value.
1649+
///
1650+
/// If the current capacity is less than the lower limit, this is a no-op.
1651+
///
1652+
/// # Errors
1653+
///
1654+
/// This function returns an error if the allocator fails to shrink the allocation,
1655+
/// the vector thereafter is still safe to use, the capacity remains unchanged
1656+
/// however. See [`Allocator::shrink`].
1657+
///
1658+
/// # Examples
1659+
///
1660+
/// ```
1661+
/// #![feature(vec_fallible_shrink)]
1662+
///
1663+
/// let mut vec = Vec::with_capacity(10);
1664+
/// vec.extend([1, 2, 3]);
1665+
/// assert!(vec.capacity() >= 10);
1666+
/// vec.try_shrink_to(4).expect("why is the test harness failing to shrink to 12 bytes");
1667+
/// assert!(vec.capacity() >= 4);
1668+
/// vec.try_shrink_to(0).expect("this is a no-op and thus the allocator isn't involved.");
1669+
/// assert!(vec.capacity() >= 3);
1670+
/// ```
1671+
#[unstable(feature = "vec_fallible_shrink", issue = "152350")]
1672+
#[inline]
1673+
pub fn try_shrink_to(&mut self, min_capacity: usize) -> Result<(), TryReserveError> {
1674+
if self.capacity() > min_capacity {
1675+
self.buf.try_shrink_to_fit(cmp::max(self.len, min_capacity))
1676+
} else {
1677+
Ok(())
1678+
}
1679+
}
1680+
16161681
/// Converts the vector into [`Box<[T]>`][owned slice].
16171682
///
16181683
/// Before doing the conversion, this method discards excess capacity like [`shrink_to_fit`].

0 commit comments

Comments
 (0)