Preserve scoped vectors on type mismatch
This commit is contained in:
+8
-6
@@ -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"
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user