Skip to content

Commit 280c6aa

Browse files
committed
vec_recycle: implementation
1 parent 29a6971 commit 280c6aa

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@
147147
#![feature(str_internals)]
148148
#![feature(temporary_niche_types)]
149149
#![feature(trivial_clone)]
150+
#![feature(transmutability)]
150151
#![feature(trusted_fused)]
151152
#![feature(trusted_len)]
152153
#![feature(trusted_random_access)]

library/alloc/src/vec/mod.rs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ use core::hash::{Hash, Hasher};
8282
#[cfg(not(no_global_oom_handling))]
8383
use core::iter;
8484
use core::marker::PhantomData;
85-
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
85+
use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, TransmuteFrom};
8686
use core::ops::{self, Index, IndexMut, Range, RangeBounds};
8787
use core::ptr::{self, NonNull};
8888
use core::slice::{self, SliceIndex};
@@ -3243,6 +3243,92 @@ impl<T, A: Allocator> Vec<T, A> {
32433243
// - `cap / N` fits the size of the allocated memory after shrinking
32443244
unsafe { Vec::from_raw_parts_in(ptr.cast(), len / N, cap / N, alloc) }
32453245
}
3246+
3247+
/// This clears out this `Vec` and recycles the allocation into a new `Vec`.
3248+
/// The item type of the resulting `Vec` needs to have the same size and
3249+
/// alignment as the item type of the original `Vec`.
3250+
///
3251+
/// # Examples
3252+
///
3253+
/// ```
3254+
/// #![feature(vec_recycle, transmutability)]
3255+
/// let a: Vec<u8> = vec![0; 100];
3256+
/// let capacity = a.capacity();
3257+
/// let addr = a.as_ptr().addr();
3258+
/// let b: Vec<i8> = a.recycle();
3259+
/// assert_eq!(b.len(), 0);
3260+
/// assert_eq!(b.capacity(), capacity);
3261+
/// assert_eq!(b.as_ptr().addr(), addr);
3262+
/// ```
3263+
///
3264+
/// The `Recyclable` bound prevents this method from being called when `T` and `U` have different sizes; e.g.:
3265+
///
3266+
/// ```compile_fail,E0277
3267+
/// #![feature(vec_recycle, transmutability)]
3268+
/// let vec: Vec<[u8; 2]> = Vec::new();
3269+
/// let _: Vec<[u8; 1]> = vec.recycle();
3270+
/// ```
3271+
/// ...or different alignments:
3272+
///
3273+
/// ```compile_fail,E0277
3274+
/// #![feature(vec_recycle, transmutability)]
3275+
/// let vec: Vec<[u16; 0]> = Vec::new();
3276+
/// let _: Vec<[u8; 0]> = vec.recycle();
3277+
/// ```
3278+
///
3279+
/// However, due to temporary implementation limitations of `Recyclable`,
3280+
/// this method is not yet callable when `T` or `U` are slices, trait objects,
3281+
/// or other exotic types; e.g.:
3282+
///
3283+
/// ```compile_fail,E0277
3284+
/// #![feature(vec_recycle, transmutability)]
3285+
/// # let inputs = ["a b c", "d e f"];
3286+
/// # fn process(_: &[&str]) {}
3287+
/// let mut storage: Vec<&[&str]> = Vec::new();
3288+
///
3289+
/// for input in inputs {
3290+
/// let mut buffer: Vec<&str> = storage.recycle();
3291+
/// buffer.extend(input.split(" "));
3292+
/// process(&buffer);
3293+
/// storage = buffer.recycle();
3294+
/// }
3295+
/// ```
3296+
#[unstable(feature = "vec_recycle", issue = "148227")]
3297+
#[expect(private_bounds)]
3298+
pub fn recycle<U>(mut self) -> Vec<U, A>
3299+
where
3300+
U: Recyclable<T>,
3301+
{
3302+
self.clear();
3303+
const {
3304+
// FIXME(const-hack, 146097): compare `Layout`s
3305+
assert!(size_of::<T>() == size_of::<U>());
3306+
assert!(align_of::<T>() == align_of::<U>());
3307+
};
3308+
let (ptr, length, capacity, alloc) = self.into_parts_with_alloc();
3309+
debug_assert_eq!(length, 0);
3310+
// SAFETY:
3311+
// - `ptr` and `alloc` were just returned from `self.into_raw_parts_with_alloc()`
3312+
// - `T` & `U` have the same layout, so `capacity` does not need to be changed and we can safely use `alloc.dealloc` later
3313+
// - the original vector was cleared, so there is no problem with "transmuting" the stored values
3314+
unsafe { Vec::from_parts_in(ptr.cast::<U>(), length, capacity, alloc) }
3315+
}
3316+
}
3317+
3318+
/// Denotes that an allocation of `From` can be recycled into an allocation of `Self`.
3319+
///
3320+
/// # Safety
3321+
///
3322+
/// `Self` is `Recyclable<From>` if `Layout::new::<Self>() == Layout::new::<From>()`.
3323+
unsafe trait Recyclable<From: Sized>: Sized {}
3324+
3325+
#[unstable_feature_bound(transmutability)]
3326+
// SAFETY: enforced by `TransmuteFrom`
3327+
unsafe impl<From, To> Recyclable<From> for To
3328+
where
3329+
for<'a> &'a MaybeUninit<To>: TransmuteFrom<&'a MaybeUninit<From>, { Assume::SAFETY }>,
3330+
for<'a> &'a MaybeUninit<From>: TransmuteFrom<&'a MaybeUninit<To>, { Assume::SAFETY }>,
3331+
{
32463332
}
32473333

32483334
impl<T: Clone, A: Allocator> Vec<T, A> {

0 commit comments

Comments
 (0)