vec_recycle: implementation

This commit is contained in:
Pavel Grigorenko 2025-10-31 15:36:55 +03:00
parent c8f22ca269
commit 33da7285ac
2 changed files with 88 additions and 1 deletions

View file

@ -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)]

View file

@ -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<T, A: Allocator> Vec<T, A> {
// - `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<u8> = vec![0; 100];
/// let capacity = a.capacity();
/// let addr = a.as_ptr().addr();
/// let b: Vec<i8> = 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<U>(mut self) -> Vec<U, A>
where
U: Recyclable<T>,
{
self.clear();
const {
// FIXME(const-hack, 146097): compare `Layout`s
assert!(size_of::<T>() == size_of::<U>());
assert!(align_of::<T>() == align_of::<U>());
};
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::<U>(), length, capacity, alloc) }
}
}
/// Denotes that an allocation of `From` can be recycled into an allocation of `Self`.
///
/// # Safety
///
/// `Self` is `Recyclable<From>` if `Layout::new::<Self>() == Layout::new::<From>()`.
unsafe trait Recyclable<From: Sized>: Sized {}
#[unstable_feature_bound(transmutability)]
// SAFETY: enforced by `TransmuteFrom`
unsafe impl<From, To> Recyclable<From> for To
where
for<'a> &'a MaybeUninit<To>: TransmuteFrom<&'a MaybeUninit<From>, { Assume::SAFETY }>,
for<'a> &'a MaybeUninit<From>: TransmuteFrom<&'a MaybeUninit<To>, { Assume::SAFETY }>,
{
}
impl<T: Clone, A: Allocator> Vec<T, A> {