Preserve scoped vectors on type mismatch

This commit is contained in:
2026-07-31 03:53:27 +02:00
parent 7d4550d1c9
commit 78159657ae
2 changed files with 60 additions and 6 deletions
+8 -6
View File
@@ -199,6 +199,7 @@ impl TypeErasedVec {
/// The previous elements are cleared. While the guard exists, its lifetime
/// tracks `T`. Outside the guard, the allocation is treated as
/// `MaybeUninit<T>` so expired references are never reconstructed or dropped.
/// A layout mismatch returns `None` without changing the erased vector.
///
/// # Panics
/// Panics if `T` needs drop.
@@ -207,14 +208,14 @@ impl TypeErasedVec {
!std::mem::needs_drop::<T>(),
"scoped element types must not require drop"
);
self.clear();
if self.layout == Layout::new::<T>() {
self.vtable = TypeErasedVecVtable::new::<std::mem::MaybeUninit<T>>();
Some(ContentGuard::new_scoped(self))
} else {
None
if self.layout != Layout::new::<T>() {
return None;
}
self.clear();
self.vtable = TypeErasedVecVtable::new::<std::mem::MaybeUninit<T>>();
Some(ContentGuard::new_scoped(self))
}
/// Access the erased capacity using a possibly non-`'static` type.
@@ -225,6 +226,7 @@ impl TypeErasedVec {
///
/// # Panics
/// Panics if `T` needs drop or does not have the erased allocation's layout.
/// A layout-mismatch panic leaves the erased vector unchanged.
#[expect(
clippy::panic,
reason = "this infallible convenience method documents and reports layout mismatch"
+52
View File
@@ -705,6 +705,58 @@ fn test_try_as_type_scoped_returns_none_for_mismatched_layout() {
assert!(erased_u32.try_as_type_scoped::<u32>().is_some());
}
#[test]
fn test_try_as_type_scoped_mismatch_preserves_nonempty_vector() {
let drop_count = Rc::new(Cell::new(0));
let mut values = Vec::with_capacity(4);
values.push(DropTracker::new(Rc::clone(&drop_count)));
let original_ptr = values.as_ptr();
let original_capacity = values.capacity();
let mut erased = TypeErasedVec::new(values);
assert!(erased.try_as_type_scoped::<u8>().is_none());
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
assert_eq!(erased.capacity(), original_capacity);
assert_eq!(drop_count.get(), 0);
{
// SAFETY: The failed scoped probe preserved the original element type.
let guard = unsafe { erased.cast_type::<DropTracker>() };
assert_eq!(guard.as_slice().as_ptr(), original_ptr);
}
drop(erased);
assert_eq!(drop_count.get(), 1);
}
#[test]
fn test_as_type_scoped_mismatch_panics_before_mutation() {
let drop_count = Rc::new(Cell::new(0));
let mut values = Vec::with_capacity(4);
values.push(DropTracker::new(Rc::clone(&drop_count)));
let original_ptr = values.as_ptr();
let original_capacity = values.capacity();
let mut erased = TypeErasedVec::new(values);
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
let _ = erased.as_type_scoped::<u8>();
}));
assert!(result.is_err());
assert_erased_state(&erased, 1, Layout::new::<DropTracker>());
assert_eq!(erased.capacity(), original_capacity);
assert_eq!(drop_count.get(), 0);
{
// SAFETY: The panicking scoped probe preserved the original element type.
let guard = unsafe { erased.cast_type::<DropTracker>() };
assert_eq!(guard.as_slice().as_ptr(), original_ptr);
}
drop(erased);
assert_eq!(drop_count.get(), 1);
}
#[test]
#[should_panic(expected = "Target type layout must exactly match")]
fn test_as_type_scoped_panics_for_mismatched_layout() {