diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index ab41f5646e5e5..1c207639f412a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -626,12 +626,10 @@ impl Box<[T]> { #[inline] pub fn try_new_uninit_slice(len: usize) -> Result]>, AllocError> { unsafe { - let layout = match Layout::array::>(len) { - Ok(l) => l, - Err(_) => return Err(AllocError), - }; - let ptr = Global.allocate(layout)?; - Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len)) + match RawVec::try_with_capacity(len) { + Ok(raw) => Ok(raw.into_box(len)), + Err(_) => Err(AllocError), + } } } @@ -658,12 +656,10 @@ impl Box<[T]> { #[inline] pub fn try_new_zeroed_slice(len: usize) -> Result]>, AllocError> { unsafe { - let layout = match Layout::array::>(len) { - Ok(l) => l, - Err(_) => return Err(AllocError), - }; - let ptr = Global.allocate_zeroed(layout)?; - Ok(RawVec::from_raw_parts_in(ptr.as_mut_ptr() as *mut _, len, Global).into_box(len)) + match RawVec::try_with_capacity_zeroed(len) { + Ok(raw) => Ok(raw.into_box(len)), + Err(_) => Err(AllocError), + } } } } diff --git a/library/alloc/src/collections/mod.rs b/library/alloc/src/collections/mod.rs index 1ea135a2aed82..e3aeae4e47fbe 100644 --- a/library/alloc/src/collections/mod.rs +++ b/library/alloc/src/collections/mod.rs @@ -52,7 +52,11 @@ pub use linked_list::LinkedList; #[doc(no_inline)] pub use vec_deque::VecDeque; +#[cfg(not(no_global_oom_handling))] +use crate::alloc::handle_alloc_error; use crate::alloc::{Layout, LayoutError}; +#[cfg(not(no_global_oom_handling))] +use crate::raw_vec::capacity_overflow; use core::fmt::Display; /// The error type for `try_reserve` methods. @@ -106,6 +110,19 @@ pub enum TryReserveErrorKind { }, } +#[cfg(not(no_global_oom_handling))] +impl TryReserveError { + /// Panic, or worse, according to the global handlers for each case. + pub(crate) fn handle(self) -> ! { + match self.kind { + TryReserveErrorKind::CapacityOverflow => capacity_overflow(), + TryReserveErrorKind::AllocError { layout, non_exhaustive: () } => { + handle_alloc_error(layout) + } + } + } +} + #[unstable( feature = "try_reserve_kind", reason = "Uncertain how much info should be exposed", diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 1bb257acff76a..d9400c1022f46 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -109,6 +109,7 @@ #![feature(iter_advance_by)] #![feature(iter_zip)] #![feature(layout_for_ptr)] +#![feature(more_fallible_allocation_methods)] #![feature(maybe_uninit_extra)] #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(new_uninit))] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 3d38e73305a37..3c6baaef8925d 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -93,6 +93,18 @@ impl RawVec { Self::with_capacity_in(capacity, Global) } + /// Tries to create a `RawVec` (on the system heap) with exactly the + /// capacity and alignment requirements for a `[T; capacity]`. This is + /// equivalent to calling `RawVec::new` when `capacity` is `0` or `T` is + /// zero-sized. Note that if `T` is zero-sized this means you will + /// *not* get a `RawVec` with the requested capacity. + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + pub fn try_with_capacity(capacity: usize) -> Result { + Self::try_with_capacity_in(capacity, Global) + } + /// Like `with_capacity`, but guarantees the buffer is zeroed. #[cfg(not(any(no_global_oom_handling, test)))] #[must_use] @@ -100,6 +112,14 @@ impl RawVec { pub fn with_capacity_zeroed(capacity: usize) -> Self { Self::with_capacity_zeroed_in(capacity, Global) } + + /// Like `try_with_capacity`, but guarantees a successfully allocated buffer is zeroed. + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + pub fn try_with_capacity_zeroed(capacity: usize) -> Result { + Self::try_with_capacity_zeroed_in(capacity, Global) + } } impl RawVec { @@ -132,6 +152,13 @@ impl RawVec { Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) } + /// Like `try_with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc) + } + /// Like `with_capacity_zeroed`, but parameterized over the choice /// of allocator for the returned `RawVec`. #[cfg(not(no_global_oom_handling))] @@ -140,6 +167,15 @@ impl RawVec { Self::allocate_in(capacity, AllocInit::Zeroed, alloc) } + /// Like `with_capacity_zeroed`, but parameterized over the choice + /// of allocator for the returned `RawVec`. + #[cfg(not(any(no_global_oom_handling, test)))] + #[must_use] + #[inline] + pub fn try_with_capacity_zeroed_in(capacity: usize, alloc: A) -> Result { + Self::try_allocate_in(capacity, AllocInit::Zeroed, alloc) + } + /// Converts the entire buffer into `Box<[MaybeUninit]>` with the specified `len`. /// /// Note that this will correctly reconstitute any `cap` changes @@ -168,33 +204,43 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self { + match Self::try_allocate_in(capacity, init, alloc) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + fn try_allocate_in( + capacity: usize, + init: AllocInit, + alloc: A, + ) -> Result { if mem::size_of::() == 0 { - Self::new_in(alloc) + Ok(Self::new_in(alloc)) } else { // We avoid `unwrap_or_else` here because it bloats the amount of // LLVM IR generated. let layout = match Layout::array::(capacity) { Ok(layout) => layout, - Err(_) => capacity_overflow(), + Err(_) => return Err(TryReserveError::from(CapacityOverflow)), }; - match alloc_guard(layout.size()) { - Ok(_) => {} - Err(_) => capacity_overflow(), - } + alloc_guard(layout.size())?; let result = match init { AllocInit::Uninitialized => alloc.allocate(layout), AllocInit::Zeroed => alloc.allocate_zeroed(layout), }; let ptr = match result { Ok(ptr) => ptr, - Err(_) => handle_alloc_error(layout), + Err(_) => { + return Err(TryReserveError::from(AllocError { layout, non_exhaustive: () })); + } }; - Self { + Ok(Self { ptr: unsafe { Unique::new_unchecked(ptr.cast().as_ptr()) }, cap: Self::capacity_from_bytes(ptr.len()), alloc, - } + }) } } @@ -349,7 +395,29 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] pub fn shrink_to_fit(&mut self, amount: usize) { - handle_reserve(self.shrink(amount)); + handle_reserve(self.try_shrink_to_fit(amount)); + } + + /// Tries to shrink the allocation down to the specified amount. If the given amount + /// is 0, actually completely deallocates. + /// + /// # Panics + /// + /// Panics if the given amount is *larger* than the current capacity. + pub fn try_shrink_to_fit(&mut self, amount: usize) -> Result<(), TryReserveError> { + assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity"); + + let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; + let new_size = amount * mem::size_of::(); + + let ptr = unsafe { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); + self.alloc + .shrink(ptr, layout, new_layout) + .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? + }; + self.set_ptr(ptr); + Ok(()) } } @@ -421,22 +489,6 @@ impl RawVec { self.set_ptr(ptr); Ok(()) } - - fn shrink(&mut self, amount: usize) -> Result<(), TryReserveError> { - assert!(amount <= self.capacity(), "Tried to shrink to a larger capacity"); - - let (ptr, layout) = if let Some(mem) = self.current_memory() { mem } else { return Ok(()) }; - let new_size = amount * mem::size_of::(); - - let ptr = unsafe { - let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); - self.alloc - .shrink(ptr, layout, new_layout) - .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })? - }; - self.set_ptr(ptr); - Ok(()) - } } // This function is outside `RawVec` to minimize compile times. See the comment @@ -513,6 +565,6 @@ fn alloc_guard(alloc_size: usize) -> Result<(), TryReserveError> { // ensure that the code generation related to these panics is minimal as there's // only one location which panics rather than a bunch throughout the module. #[cfg(not(no_global_oom_handling))] -fn capacity_overflow() -> ! { +pub fn capacity_overflow() -> ! { panic!("capacity overflow"); } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index ae730be0d25a5..be3e4e48c8eab 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -98,6 +98,7 @@ use crate::alloc::Global; #[cfg(not(no_global_oom_handling))] use crate::borrow::ToOwned; use crate::boxed::Box; +use crate::collections::TryReserveError; use crate::vec::Vec; #[unstable(feature = "slice_range", issue = "76393")] @@ -157,6 +158,7 @@ mod hack { use core::alloc::Allocator; use crate::boxed::Box; + use crate::collections::TryReserveError; use crate::vec::Vec; // We shouldn't add inline attribute to this since this is used in @@ -176,6 +178,14 @@ mod hack { T::to_vec(s, alloc) } + #[inline] + pub fn try_to_vec( + s: &[T], + alloc: A, + ) -> Result, TryReserveError> { + T::try_to_vec(s, alloc) + } + #[cfg(not(no_global_oom_handling))] pub trait ConvertVec { fn to_vec(s: &[Self], alloc: A) -> Vec @@ -183,6 +193,12 @@ mod hack { Self: Sized; } + pub trait TryConvertVec { + fn try_to_vec(s: &[Self], alloc: A) -> Result, TryReserveError> + where + Self: Sized; + } + #[cfg(not(no_global_oom_handling))] impl ConvertVec for T { #[inline] @@ -235,6 +251,45 @@ mod hack { v } } + + impl TryConvertVec for T { + #[inline] + default fn try_to_vec( + s: &[Self], + alloc: A, + ) -> Result, TryReserveError> { + struct DropGuard<'a, T, A: Allocator> { + vec: &'a mut Vec, + num_init: usize, + } + impl<'a, T, A: Allocator> Drop for DropGuard<'a, T, A> { + #[inline] + fn drop(&mut self) { + // SAFETY: + // items were marked initialized in the loop below + unsafe { + self.vec.set_len(self.num_init); + } + } + } + let mut vec = Vec::try_with_capacity_in(s.len(), alloc)?; + let mut guard = DropGuard { vec: &mut vec, num_init: 0 }; + let slots = guard.vec.spare_capacity_mut(); + // .take(slots.len()) is necessary for LLVM to remove bounds checks + // and has better codegen than zip. + for (i, b) in s.iter().enumerate().take(slots.len()) { + guard.num_init = i; + slots[i].write(b.clone()); + } + core::mem::forget(guard); + // SAFETY: + // the vec was allocated and initialized above to at least this length. + unsafe { + vec.set_len(s.len()); + } + Ok(vec) + } + } } #[lang = "slice_alloc"] @@ -474,6 +529,26 @@ impl [T] { self.to_vec_in(Global) } + /// Tries to copy `self` into a new `Vec`. + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let s = [10, 40, 30]; + /// let x = s.try_to_vec().unwrap(); + /// // Here, `s` and `x` can be modified independently. + /// ``` + #[inline] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_to_vec(&self) -> Result, TryReserveError> + where + T: Clone, + { + self.try_to_vec_in(Global) + } + /// Copies `self` into a new `Vec` with an allocator. /// /// # Examples @@ -498,6 +573,30 @@ impl [T] { hack::to_vec(self, alloc) } + /// Tries to copy `self` into a new `Vec` with an allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// #![feature(more_fallible_allocation_methods)] + /// + /// use std::alloc::System; + /// + /// let s = [10, 40, 30]; + /// let x = s.try_to_vec_in(System).unwrap(); + /// // Here, `s` and `x` can be modified independently. + /// ``` + #[inline] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_to_vec_in(&self, alloc: A) -> Result, TryReserveError> + where + T: Clone, + { + // N.B., see the `hack` module in this file for more details. + hack::try_to_vec(self, alloc) + } + /// Converts `self` into a vector without clones or allocation. /// /// The resulting vector can be converted back into a box via diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 69495f31c32ca..17cfbacf2b9eb 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -36,6 +36,7 @@ use core::unicode::conversions; use crate::borrow::ToOwned; use crate::boxed::Box; +use crate::collections::TryReserveError; use crate::slice::{Concat, Join, SliceIndex}; use crate::string::String; use crate::vec::Vec; @@ -590,6 +591,24 @@ impl str { // make_ascii_lowercase() preserves the UTF-8 invariant. unsafe { String::from_utf8_unchecked(bytes) } } + + /// Tries to create a `String`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let s: &str = "a"; + /// let ss: String = s.try_to_owned().unwrap(); + /// ``` + #[inline] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_to_owned(&self) -> Result { + unsafe { Ok(String::from_utf8_unchecked(self.as_bytes().try_to_vec()?)) } + } } /// Converts a boxed slice of bytes to a boxed string slice without checking diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index b151842458d35..6eaf23f993c15 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -46,9 +46,9 @@ use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; use core::fmt; use core::hash; +use core::iter::FusedIterator; #[cfg(not(no_global_oom_handling))] -use core::iter::FromIterator; -use core::iter::{from_fn, FusedIterator}; +use core::iter::{from_fn, FromIterator}; #[cfg(not(no_global_oom_handling))] use core::ops::Add; #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 7c065f37d1fa8..5e4201cf62b88 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -34,6 +34,7 @@ use crate::alloc::{box_free, WriteCloneIntoRaw}; use crate::alloc::{AllocError, Allocator, Global, Layout}; use crate::borrow::{Cow, ToOwned}; use crate::boxed::Box; +use crate::collections::{TryReserveError, TryReserveErrorKind}; use crate::rc::is_dangling; #[cfg(not(no_global_oom_handling))] use crate::string::String; @@ -1203,11 +1204,31 @@ impl Arc<[T]> { #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { unsafe { - Self::allocate_for_layout( - Layout::array::(len).unwrap(), - |layout| Global.allocate(layout), + match Self::try_allocate_for_slice(len) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + } + + /// Tries to allocate an `ArcInner<[T]>` with the given length. + unsafe fn try_allocate_for_slice(len: usize) -> Result<*mut ArcInner<[T]>, TryReserveError> { + unsafe { + let layout = match Layout::array::(len) { + Ok(layout) => layout, + Err(_) => return Err(TryReserveError::from(TryReserveErrorKind::CapacityOverflow)), + }; + Self::try_allocate_for_layout( + layout, + |l| Global.allocate(l), |mem| ptr::slice_from_raw_parts_mut(mem as *mut T, len) as *mut ArcInner<[T]>, ) + .map_err(|_| { + TryReserveError::from(TryReserveErrorKind::AllocError { + layout, + non_exhaustive: (), + }) + }) } } @@ -1217,11 +1238,23 @@ impl Arc<[T]> { #[cfg(not(no_global_oom_handling))] unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { unsafe { - let ptr = Self::allocate_for_slice(v.len()); + match Self::try_copy_from_slice(v) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + } + + /// Tries to copy elements from slice into newly allocated Arc<\[T\]> + /// + /// Unsafe because the caller must either take ownership or bind `T: Copy`. + unsafe fn try_copy_from_slice(v: &[T]) -> Result, TryReserveError> { + unsafe { + let ptr = Self::try_allocate_for_slice(v.len())?; ptr::copy_nonoverlapping(v.as_ptr(), &mut (*ptr).data as *mut [T] as *mut T, v.len()); - Self::from_ptr(ptr) + Ok(Self::from_ptr(ptr)) } } @@ -2467,14 +2500,38 @@ impl From> for Arc<[T]> { /// assert_eq!(&[1, 2, 3], &shared[..]); /// ``` #[inline] - fn from(mut v: Vec) -> Arc<[T]> { + fn from(v: Vec) -> Arc<[T]> { + match Arc::try_from_vec(v) { + Ok(r) => r, + Err(e) => e.handle(), + } + } +} + +// Avoid `error: specializing impl repeats parameter` implementing `TryFrom`. +impl Arc<[T]> { + /// Tries to allocate a reference-counted slice and move `v`'s items into it. + /// + /// # Example + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// # use std::sync::Arc; + /// let unique: Vec = vec![1, 2, 3]; + /// let shared: Arc<[i32]> = Arc::try_from_vec(unique).unwrap(); + /// assert_eq!(&[1, 2, 3], &shared[..]); + /// ``` + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + #[inline] + pub fn try_from_vec(mut v: Vec) -> Result { unsafe { - let arc = Arc::copy_from_slice(&v); + let arc = Arc::try_copy_from_slice(&v)?; // Allow the Vec to free its memory, but not destroy its contents v.set_len(0); - arc + Ok(arc) } } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index b6b11b75c99c9..3879714683c7c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -144,6 +144,7 @@ mod spec_from_iter; #[cfg(not(no_global_oom_handling))] use self::spec_extend::SpecExtend; +use self::spec_extend::TrySpecExtend; #[cfg(not(no_global_oom_handling))] mod spec_extend; @@ -470,6 +471,51 @@ impl Vec { Self::with_capacity_in(capacity, Global) } + /// Tries to construct a new, empty `Vec` with the specified capacity. + /// + /// The vector will be able to hold exactly `capacity` elements without + /// reallocating. If `capacity` is 0, the vector will not allocate. + /// + /// It is important to note that although the returned vector has the + /// *capacity* specified, the vector will have a zero *length*. For an + /// explanation of the difference between length and capacity, see + /// *[Capacity and reallocation]*. + /// + /// [Capacity and reallocation]: #capacity-and-reallocation + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = Vec::try_with_capacity(10).unwrap(); + /// + /// // The vector contains no items, even though it has capacity for more + /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); + /// + /// // These are all done without reallocating... + /// for i in 0..10 { + /// vec.push(i); + /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); + /// + /// // ...but this may make the vector reallocate + /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); + /// + /// let result = Vec::::try_with_capacity(usize::MAX); + /// assert!(result.is_err()); + /// ``` + #[inline] + #[doc(alias = "malloc")] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_with_capacity(capacity: usize) -> Result { + Self::try_with_capacity_in(capacity, Global) + } + /// Creates a `Vec` directly from the raw components of another vector. /// /// # Safety @@ -609,6 +655,55 @@ impl Vec { Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 } } + /// Tries to construct a new, empty `Vec` with the specified capacity + /// with the provided allocator. + /// + /// The vector will be able to hold exactly `capacity` elements without + /// reallocating. If `capacity` is 0, the vector will not allocate. + /// + /// It is important to note that although the returned vector has the + /// *capacity* specified, the vector will have a zero *length*. For an + /// explanation of the difference between length and capacity, see + /// *[Capacity and reallocation]*. + /// + /// [Capacity and reallocation]: #capacity-and-reallocation + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// #![feature(more_fallible_allocation_methods)] + /// + /// use std::alloc::System; + /// use alloc::collections::TryReserveError; + /// + /// let mut vec = Vec::try_with_capacity_in(10, System).unwrap(); + /// + /// // The vector contains no items, even though it has capacity for more + /// assert_eq!(vec.len(), 0); + /// assert_eq!(vec.capacity(), 10); + /// + /// // These are all done without reallocating... + /// for i in 0..10 { + /// vec.push(i); + /// } + /// assert_eq!(vec.len(), 10); + /// assert_eq!(vec.capacity(), 10); + /// + /// // ...but this may make the vector reallocate + /// vec.push(11); + /// assert_eq!(vec.len(), 11); + /// assert!(vec.capacity() >= 11); + /// + /// let result: Result, TryReserveError> = Vec::try_with_capacity_in(usize::MAX, System); + /// assert!(result.is_err()); + /// ``` + #[inline] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 }) + } + /// Creates a `Vec` directly from the raw components of another vector. /// /// # Safety @@ -932,12 +1027,39 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn shrink_to_fit(&mut self) { + match self.try_shrink_to_fit() { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + /// Tries to shrink the capacity of the vector as much as possible. + /// + /// It will drop down as close as possible to the length but the allocator + /// may still inform the vector that there is space for a few more elements. + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// assert_eq!(vec.capacity(), 10); + /// vec.try_shrink_to_fit().unwrap(); + /// assert!(vec.capacity() >= 3); + /// ``` + #[doc(alias = "realloc")] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_shrink_to_fit(&mut self) -> Result<(), TryReserveError> { // The capacity is never less than the length, and there's nothing to do when - // they are equal, so we can avoid the panic case in `RawVec::shrink_to_fit` + // they are equal, so we can avoid the panic case in `RawVec::try_shrink_to_fit` // by only calling it with a greater capacity. - if self.capacity() > self.len { - self.buf.shrink_to_fit(self.len); + if self.capacity() <= self.len { + return Ok(()); } + + self.buf.try_shrink_to_fit(self.len) } /// Shrinks the capacity of the vector with a lower bound. @@ -992,13 +1114,49 @@ impl Vec { /// ``` #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_boxed_slice(mut self) -> Box<[T], A> { + pub fn into_boxed_slice(self) -> Box<[T], A> { + match self.try_into_boxed_slice() { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + /// Tries to convert the vector into [`Box<[T]>`][owned slice]. + /// + /// Note that this will drop any excess capacity. + /// + /// [owned slice]: Box + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let v = vec![1, 2, 3]; + /// + /// let slice = v.try_into_boxed_slice().unwrap(); + /// ``` + /// + /// Any excess capacity is removed: + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3]); + /// + /// assert_eq!(vec.capacity(), 10); + /// let slice = vec.try_into_boxed_slice().unwrap(); + /// assert_eq!(slice.into_vec().capacity(), 3); + /// ``` + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_into_boxed_slice(mut self) -> Result, TryReserveError> { unsafe { - self.shrink_to_fit(); + self.try_shrink_to_fit()?; let me = ManuallyDrop::new(self); let buf = ptr::read(&me.buf); let len = me.len(); - buf.into_box(len).assume_init() + Ok(buf.into_box(len).assume_init()) } } @@ -1723,8 +1881,26 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn push(&mut self, value: T) { - // This will panic or abort if we would allocate > isize::MAX bytes - // or if the length increment would overflow for zero-sized types. + match self.try_push(value) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + /// Tries to append an element to the back of a collection. + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = vec![1, 2]; + /// vec.try_push(3).unwrap(); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + #[inline] + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_push(&mut self, value: T) -> Result<(), TryReserveError> { if self.len == self.buf.capacity() { self.buf.reserve_for_push(self.len); } @@ -1733,6 +1909,7 @@ impl Vec { ptr::write(end, value); self.len += 1; } + Ok(()) } /// Removes the last element from a vector and returns it, or [`None`] if it @@ -1787,11 +1964,23 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { + unsafe { + match self.try_append_elements(other) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + } + + /// Tries to append elements to `Self` from other buffer. + #[inline] + unsafe fn try_append_elements(&mut self, other: *const [T]) -> Result<(), TryReserveError> { let count = unsafe { (*other).len() }; - self.reserve(count); + self.try_reserve(count)?; let len = self.len(); unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; self.len += count; + Ok(()) } /// Creates a draining iterator that removes the specified range in the vector @@ -2196,6 +2385,46 @@ impl Vec { } } + /// Tries to resize the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with `value`. + /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// + /// This method requires `T` to implement [`Clone`], + /// in order to be able to clone the passed value. + /// If you need more flexibility (or want to rely on [`Default`] instead of + /// [`Clone`]), use [`Vec::resize_with`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = vec!["hello"]; + /// vec.try_resize(3, "world").unwrap(); + /// assert_eq!(vec, ["hello", "world", "world"]); + /// + /// let mut vec = vec![1, 2, 3, 4]; + /// vec.try_resize(2, 0).unwrap(); + /// assert_eq!(vec, [1, 2]); + /// + /// let mut vec = vec![42]; + /// let result = vec.try_resize(usize::MAX, 0); + /// assert!(result.is_err()); + /// ``` + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_resize(&mut self, new_len: usize, value: T) -> Result<(), TryReserveError> { + let len = self.len(); + + if new_len > len { + self.try_extend_with(new_len - len, ExtendElement(value)) + } else { + self.truncate(new_len); + Ok(()) + } + } + /// Clones and appends all elements in a slice to the `Vec`. /// /// Iterates over the slice `other`, clones each element, and then appends @@ -2221,6 +2450,32 @@ impl Vec { self.spec_extend(other.iter()) } + /// Tries to clone and append all elements in a slice to the `Vec`. + /// + /// Iterates over the slice `other`, clones each element, and then appends + /// it to this `Vec`. The `other` vector is traversed in-order. + /// + /// Note that this function is same as [`extend`] except that it is + /// specialized to work with slices instead. If and when Rust gets + /// specialization this function will likely be deprecated (but still + /// available). + /// + /// # Examples + /// + /// ``` + /// #![feature(more_fallible_allocation_methods)] + /// + /// let mut vec = vec![1]; + /// vec.try_extend_from_slice(&[2, 3, 4]).unwrap(); + /// assert_eq!(vec, [1, 2, 3, 4]); + /// ``` + /// + /// [`extend`]: Vec::extend + #[unstable(feature = "more_fallible_allocation_methods", issue = "86942")] + pub fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveError> { + self.try_spec_extend(other.iter()) + } + /// Copies elements from `src` range to the end of the vector. /// /// # Panics @@ -2298,8 +2553,20 @@ impl T> ExtendWith for ExtendFunc { impl Vec { #[cfg(not(no_global_oom_handling))] /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, mut value: E) { - self.reserve(n); + fn extend_with>(&mut self, n: usize, value: E) { + match self.try_extend_with(n, value) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + /// Try to extend the vector by `n` values, using the given generator. + fn try_extend_with>( + &mut self, + n: usize, + mut value: E, + ) -> Result<(), TryReserveError> { + self.try_reserve(n)?; unsafe { let mut ptr = self.as_mut_ptr().add(self.len()); @@ -2323,6 +2590,7 @@ impl Vec { } // len set by scope guard + Ok(()) } } } @@ -2635,7 +2903,19 @@ impl Vec { // leaf method to which various SpecFrom/SpecExtend implementations delegate when // they have no further optimizations to apply #[cfg(not(no_global_oom_handling))] - fn extend_desugared>(&mut self, mut iterator: I) { + fn extend_desugared>(&mut self, iterator: I) { + match self.try_extend_desugared(iterator) { + Ok(r) => r, + Err(e) => e.handle(), + } + } + + // leaf method to which various SpecFrom/SpecExtend implementations delegate when + // they have no further optimizations to apply + fn try_extend_desugared>( + &mut self, + mut iterator: I, + ) -> Result<(), TryReserveError> { // This is the case for a general iterator. // // This function should be the moral equivalent of: @@ -2647,16 +2927,16 @@ impl Vec { let len = self.len(); if len == self.capacity() { let (lower, _) = iterator.size_hint(); - self.reserve(lower.saturating_add(1)); + self.try_reserve(lower.saturating_add(1))?; } unsafe { ptr::write(self.as_mut_ptr().add(len), element); - // Since next() executes user code which can panic we have to bump the length - // after each step. // NB can't overflow since we would have had to alloc the address space self.set_len(len + 1); } } + + Ok(()) } /// Creates a splicing iterator that replaces the specified range in the vector diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index c3b4534096de5..db24cbaf88c21 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -1,4 +1,6 @@ use crate::alloc::Allocator; +use crate::collections::TryReserveErrorKind; +use crate::vec::TryReserveError; use core::iter::TrustedLen; use core::ptr::{self}; use core::slice::{self}; @@ -6,10 +8,17 @@ use core::slice::{self}; use super::{IntoIter, SetLenOnDrop, Vec}; // Specialization trait used for Vec::extend +#[cfg(not(no_global_oom_handling))] pub(super) trait SpecExtend { fn spec_extend(&mut self, iter: I); } +// Specialization trait used for Vec::try_extend +pub(super) trait TrySpecExtend { + fn try_spec_extend(&mut self, iter: I) -> Result<(), TryReserveError>; +} + +#[cfg(not(no_global_oom_handling))] impl SpecExtend for Vec where I: Iterator, @@ -19,6 +28,16 @@ where } } +impl TrySpecExtend for Vec +where + I: Iterator, +{ + default fn try_spec_extend(&mut self, iter: I) -> Result<(), TryReserveError> { + self.try_extend_desugared(iter) + } +} + +#[cfg(not(no_global_oom_handling))] impl SpecExtend for Vec where I: TrustedLen, @@ -57,6 +76,39 @@ where } } +impl TrySpecExtend for Vec +where + I: TrustedLen, +{ + default fn try_spec_extend(&mut self, iterator: I) -> Result<(), TryReserveError> { + // This is the case for a TrustedLen iterator. + let (low, high) = iterator.size_hint(); + if let Some(additional) = high { + debug_assert_eq!( + low, + additional, + "TrustedLen iterator's size hint is not exact: {:?}", + (low, high) + ); + self.try_reserve(additional)?; + unsafe { + let mut ptr = self.as_mut_ptr().add(self.len()); + let mut local_len = SetLenOnDrop::new(&mut self.len); + iterator.for_each(move |element| { + ptr::write(ptr, element); + ptr = ptr.offset(1); + // NB can't overflow since we would have had to alloc the address space + local_len.increment_len(1); + }); + } + Ok(()) + } else { + Err(TryReserveError::from(TryReserveErrorKind::CapacityOverflow)) + } + } +} + +#[cfg(not(no_global_oom_handling))] impl SpecExtend> for Vec { fn spec_extend(&mut self, mut iterator: IntoIter) { unsafe { @@ -66,6 +118,17 @@ impl SpecExtend> for Vec { } } +impl TrySpecExtend> for Vec { + fn try_spec_extend(&mut self, mut iterator: IntoIter) -> Result<(), TryReserveError> { + unsafe { + self.try_append_elements(iterator.as_slice() as _)?; + } + iterator.ptr = iterator.end; + Ok(()) + } +} + +#[cfg(not(no_global_oom_handling))] impl<'a, T: 'a, I, A: Allocator + 'a> SpecExtend<&'a T, I> for Vec where I: Iterator, @@ -76,6 +139,17 @@ where } } +impl<'a, T: 'a, I, A: Allocator + 'a> TrySpecExtend<&'a T, I> for Vec +where + I: Iterator, + T: Clone, +{ + default fn try_spec_extend(&mut self, iterator: I) -> Result<(), TryReserveError> { + self.try_spec_extend(iterator.cloned()) + } +} + +#[cfg(not(no_global_oom_handling))] impl<'a, T: 'a, A: Allocator + 'a> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec where T: Copy, @@ -85,3 +159,13 @@ where unsafe { self.append_elements(slice) }; } } + +impl<'a, T: 'a, A: Allocator + 'a> TrySpecExtend<&'a T, slice::Iter<'a, T>> for Vec +where + T: Copy, +{ + fn try_spec_extend(&mut self, iterator: slice::Iter<'a, T>) -> Result<(), TryReserveError> { + let slice = iterator.as_slice(); + unsafe { self.try_append_elements(slice) } + } +} diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 68e48348b076e..718f8817e7681 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -26,6 +26,7 @@ #![feature(const_default_impls)] #![feature(const_trait_impl)] #![feature(const_str_from_utf8)] +#![feature(more_fallible_allocation_methods)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher};