Encapsulate VecParts invariants

This commit is contained in:
2026-07-31 04:12:16 +02:00
parent 63c335c023
commit dd9a2508bb
3 changed files with 43 additions and 17 deletions
+6 -6
View File
@@ -56,8 +56,8 @@ impl fmt::Debug for TypeErasedVec {
formatter
.debug_struct("TypeErasedVec")
.field("layout", &self.layout)
.field("length", &self.parts.len)
.field("capacity", &self.parts.cap)
.field("length", &self.parts.length())
.field("capacity", &self.parts.capacity())
.finish_non_exhaustive()
}
}
@@ -113,19 +113,19 @@ impl TypeErasedVec {
/// Returns the allocation's capacity in elements.
#[must_use]
pub const fn capacity(&self) -> usize {
self.parts.cap
self.parts.capacity()
}
/// Returns the number of initialized elements.
#[must_use]
pub const fn length(&self) -> usize {
self.parts.len
self.parts.length()
}
/// Returns `true` when there are no initialized elements.
#[must_use]
pub const fn is_empty(&self) -> bool {
self.parts.len == 0
self.parts.length() == 0
}
/// Returns the allocation's capacity in bytes.
@@ -134,7 +134,7 @@ impl TypeErasedVec {
/// logical capacity.
#[must_use]
pub const fn capacity_bytes(&self) -> usize {
let capacity_bytes = self.parts.cap.checked_mul(self.layout.size());
let capacity_bytes = self.parts.capacity().checked_mul(self.layout.size());
// SAFETY: A valid Vec allocation cannot exceed usize::MAX bytes.
// ZST vectors multiply their logical capacity by zero.
+30 -5
View File
@@ -1,10 +1,15 @@
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
/// A linear ownership descriptor for a type-erased `Vec` allocation.
///
/// Its pointer originates from `Vec`'s global allocator, its length covers
/// initialized elements of the erased type, its capacity uses that type's exact
/// layout, and exactly one descriptor owns and may reconstruct the allocation.
pub(super) struct VecParts {
pub(super) ptr: NonNull<u8>,
pub(super) len: usize,
pub(super) cap: usize,
ptr: NonNull<u8>,
len: usize,
cap: usize,
}
impl VecParts {
@@ -22,19 +27,39 @@ impl VecParts {
Self { ptr, len, cap }
}
pub(super) const fn length(&self) -> usize {
self.len
}
pub(super) const fn capacity(&self) -> usize {
self.cap
}
pub(super) const fn pointer(&self) -> NonNull<u8> {
self.ptr
}
pub(super) unsafe fn into_vec<T>(self) -> Vec<T> {
// SAFETY: The VecParts invariant guarantees allocator origin, unique
// ownership, initialized length, capacity, and single reconstruction.
// The caller selects T with the erased allocation's exact layout and
// guarantees that every initialized element is valid as T.
unsafe { Vec::from_raw_parts(self.ptr.as_ptr().cast::<T>(), self.len, self.cap) }
}
#[must_use]
pub(super) const unsafe fn as_slice<T>(&self) -> &[T] {
// SAFETY: Ensured by the caller
// SAFETY: The VecParts invariant guarantees a live allocation and
// initialized length. The caller selects the exact erased type T and
// guarantees no mutable access overlaps the returned shared borrow.
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.len) }
}
#[must_use]
pub(super) const unsafe fn as_slice_mut<T>(&mut self) -> &mut [T] {
// SAFETY: Ensured by the caller
// SAFETY: The VecParts invariant guarantees a live allocation and
// initialized length. The caller selects the exact erased type T and
// guarantees unique access with no overlapping references.
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.len) }
}
}
+7 -6
View File
@@ -15,9 +15,10 @@ impl<'parts, T> VecPartsRestoreGuard<'parts, T> {
unsafe fn new(parts: &'parts mut VecParts) -> Self {
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 raw parts, and ownership was transferred out of
// `parts` before reconstruction.
// SAFETY: The VecParts invariant guarantees global-allocator origin,
// valid initialized length and capacity, unique ownership, and single
// reconstruction. The vtable selects T with the exact erased layout,
// and replacement transferred ownership out of `parts`.
let vec = unsafe { original_parts.into_vec::<T>() };
Self {
@@ -73,8 +74,8 @@ impl TypeErasedVecVtable {
}
unsafe fn clear_vec<T>(parts: &mut VecParts) {
let original_ptr = parts.ptr.as_ptr();
let original_capacity = parts.cap;
let original_ptr = parts.pointer().as_ptr();
let original_capacity = parts.capacity();
// SAFETY: This vtable function is selected only for raw parts that
// were produced by Vec<T>.
@@ -89,7 +90,7 @@ impl TypeErasedVecVtable {
}
unsafe fn reserve_vec<T>(parts: &mut VecParts, additional: usize) {
let original_len = parts.len;
let original_len = parts.length();
// SAFETY: This vtable function is selected only for raw parts that
// were produced by Vec<T>.