Make erased allocation ownership linear

This commit is contained in:
2026-07-31 04:08:28 +02:00
parent e7f53d41de
commit 63c335c023
4 changed files with 17 additions and 9 deletions
+8 -3
View File
@@ -66,9 +66,14 @@ impl<'vec, T> ContentGuard<'vec, T> {
pub fn take(&mut self) -> Vec<T> {
let old_erased = std::mem::replace(self.erased, (self.reerase)(Vec::new()));
let erased_owner = ManuallyDrop::new(old_erased);
// SAFETY: The erased pointer, length, and capacity are valid for a Vec<T>.
// ManuallyDrop prevents the old TypeErasedVec from double-freeing the allocation.
unsafe { erased_owner.parts.into_vec() }
// SAFETY: ManuallyDrop prevents the old TypeErasedVec from running its
// destructor. This reads its uniquely owned descriptor exactly once.
let parts = unsafe { std::ptr::read(&raw const erased_owner.parts) };
// SAFETY: The erased pointer, length, and capacity are valid for Vec<T>,
// and `parts` uniquely owns the allocation.
unsafe { parts.into_vec() }
}
/// Calls `function` with the underlying vector and then type-erases it again.
+1 -1
View File
@@ -329,6 +329,6 @@ impl TypeErasedVec {
impl Drop for TypeErasedVec {
fn drop(&mut self) {
// SAFETY: The correct drop vector executes the destructors and frees the allocation.
unsafe { (self.vtable.drop)(self.parts) };
unsafe { (self.vtable.drop)(&mut self.parts) };
}
}
-1
View File
@@ -1,7 +1,6 @@
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
#[derive(Clone, Copy)]
pub(super) struct VecParts {
pub(super) ptr: NonNull<u8>,
pub(super) len: usize,
+8 -4
View File
@@ -58,14 +58,18 @@ pub(super) struct TypeErasedVecVtable {
/// Function pointer to clear the underlying capacity of its old elements
pub(super) clear: unsafe fn(parts: &mut VecParts),
/// Function pointer to the original type's drop logic.
pub(super) drop: unsafe fn(parts: VecParts),
pub(super) drop: unsafe fn(parts: &mut VecParts),
}
impl TypeErasedVecVtable {
pub(super) fn new<T>() -> Self {
unsafe fn drop_vec<T>(parts: VecParts) {
// SAFETY: We reconstruct the Vec to let its Drop impl handle deallocation.
_ = unsafe { parts.into_vec::<T>() };
unsafe fn drop_vec<T>(parts: &mut VecParts) {
let original_parts = std::mem::replace(parts, VecParts::from_vec(Vec::<T>::new()));
// SAFETY: The vtable selects T as the exact type that originally
// produced these uniquely owned raw parts. Reconstructing the Vec
// transfers that ownership to its Drop implementation.
_ = unsafe { original_parts.into_vec::<T>() };
}
unsafe fn clear_vec<T>(parts: &mut VecParts) {