Preserve ordinary vectors on type mismatch
This commit is contained in:
+11
-3
@@ -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"
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user