Preserve ordinary vectors on type mismatch

This commit is contained in:
2026-07-31 03:52:41 +02:00
parent 5b7c8e0375
commit 7d4550d1c9
2 changed files with 63 additions and 3 deletions
+11 -3
View File
@@ -157,19 +157,27 @@ impl TypeErasedVec {
/// Access the erased capacity with a temporarily fixed type.
/// Clears all elements currently stored in the `TypeErasedVec`.
///
/// Will fail if `T` does not have the same `Layout` as the underlying capacity.
/// Returns `None` if `T` does not have the same [`Layout`] as the
/// underlying capacity. On failure, the erased vector is unchanged.
pub fn try_as_type<T: 'static>(&mut self) -> Option<ContentGuard<'_, T>> {
if self.layout != Layout::new::<T>() {
return None;
}
self.clear();
self.vtable = TypeErasedVecVtable::new::<T>();
// SAFETY: The vector has been cleared, meaning there are no existing elements
// that could be invalidated or improperly dropped by the cast.
(self.layout == Layout::new::<T>()).then(|| ContentGuard::new(self))
Some(ContentGuard::new(self))
}
/// Access the erased capacity with a temporarily fixed type.
/// Clears all elements currently stored in the `TypeErasedVec`.
///
/// # Panics
/// Panics if `T` does not have the same `Layout` as the underlying capacity.
/// Panics if `T` does not have the same `Layout` as the underlying
/// capacity. 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
@@ -363,6 +363,58 @@ fn test_try_as_type_clears_elements() {
assert_eq!(drop_count.get(), 3);
}
#[test]
fn test_try_as_type_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::<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 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_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::<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 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_guard_with_unwind_safety() {
let drop_count = Rc::new(Cell::new(0));