diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 666ae27fb8634..b61d95c3badf2 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -146,6 +146,7 @@ #![feature(std_internals)] #![feature(str_internals)] #![feature(temporary_niche_types)] +#![feature(transmutability)] #![feature(trivial_clone)] #![feature(trusted_fused)] #![feature(trusted_len)] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 43a68ff203738..48241c124901a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -82,7 +82,7 @@ use core::hash::{Hash, Hasher}; #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, TransmuteFrom}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; @@ -3243,6 +3243,92 @@ impl Vec { // - `cap / N` fits the size of the allocated memory after shrinking unsafe { Vec::from_raw_parts_in(ptr.cast(), len / N, cap / N, alloc) } } + + /// This clears out this `Vec` and recycles the allocation into a new `Vec`. + /// The item type of the resulting `Vec` needs to have the same size and + /// alignment as the item type of the original `Vec`. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_recycle, transmutability)] + /// let a: Vec = vec![0; 100]; + /// let capacity = a.capacity(); + /// let addr = a.as_ptr().addr(); + /// let b: Vec = a.recycle(); + /// assert_eq!(b.len(), 0); + /// assert_eq!(b.capacity(), capacity); + /// assert_eq!(b.as_ptr().addr(), addr); + /// ``` + /// + /// The `Recyclable` bound prevents this method from being called when `T` and `U` have different sizes; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// let vec: Vec<[u8; 2]> = Vec::new(); + /// let _: Vec<[u8; 1]> = vec.recycle(); + /// ``` + /// ...or different alignments: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// let vec: Vec<[u16; 0]> = Vec::new(); + /// let _: Vec<[u8; 0]> = vec.recycle(); + /// ``` + /// + /// However, due to temporary implementation limitations of `Recyclable`, + /// this method is not yet callable when `T` or `U` are slices, trait objects, + /// or other exotic types; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// # let inputs = ["a b c", "d e f"]; + /// # fn process(_: &[&str]) {} + /// let mut storage: Vec<&[&str]> = Vec::new(); + /// + /// for input in inputs { + /// let mut buffer: Vec<&str> = storage.recycle(); + /// buffer.extend(input.split(" ")); + /// process(&buffer); + /// storage = buffer.recycle(); + /// } + /// ``` + #[unstable(feature = "vec_recycle", issue = "148227")] + #[expect(private_bounds)] + pub fn recycle(mut self) -> Vec + where + U: Recyclable, + { + self.clear(); + const { + // FIXME(const-hack, 146097): compare `Layout`s + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + }; + let (ptr, length, capacity, alloc) = self.into_parts_with_alloc(); + debug_assert_eq!(length, 0); + // SAFETY: + // - `ptr` and `alloc` were just returned from `self.into_raw_parts_with_alloc()` + // - `T` & `U` have the same layout, so `capacity` does not need to be changed and we can safely use `alloc.dealloc` later + // - the original vector was cleared, so there is no problem with "transmuting" the stored values + unsafe { Vec::from_parts_in(ptr.cast::(), length, capacity, alloc) } + } +} + +/// Denotes that an allocation of `From` can be recycled into an allocation of `Self`. +/// +/// # Safety +/// +/// `Self` is `Recyclable` if `Layout::new::() == Layout::new::()`. +unsafe trait Recyclable: Sized {} + +#[unstable_feature_bound(transmutability)] +// SAFETY: enforced by `TransmuteFrom` +unsafe impl Recyclable for To +where + for<'a> &'a MaybeUninit: TransmuteFrom<&'a MaybeUninit, { Assume::SAFETY }>, + for<'a> &'a MaybeUninit: TransmuteFrom<&'a MaybeUninit, { Assume::SAFETY }>, +{ } impl Vec {