Make erased allocation ownership linear
This commit is contained in:
+8
-3
@@ -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
@@ -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,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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user