Change:
Looking here, the internal length of the vector is only adjusted at the end of the loop This has the caveat that if cloning panics, the Vec will start to unwind and only drop the first elements which doesn't include the most recently appended ones. Something more in line with how the stdlib does things might look like this: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
This is not unsafe, leaking items is safe:
Sure; never said it was unsafe. This is more about making sure programs who gracefully handle panics don't wind up eventually exhausting resources.
Assigning as part of the .
I would like to fix this claim
The fix seems pretty straight-forward. The Clone specialization just needs a RAII type that handles adjusting the Vec's new len in its Drop implementation, as I've linked in my MiniVec. Copy can't fail so there's no need to adjust that specialization. The only thing that could theoretically panic in that case would be reallocating and in that case, there's no need to worry about the len.

Discussion:
#[unstable(feature = "vec_split_at_spare", issue = "81944")] #[inline] pub fn split_at_spare_mut(&mut self) -> (&mut [T], &mut [MaybeUninit<T>]) { <ins> // SAFETY: // - len is ignored and so never changed let (init, spare, _) = unsafe { self.split_at_spare_mut_with_len() }; (init, spare) } /// Safety: changing returned .2 (&mut usize) is considered the same as calling `.set_len(_)`. /// /// This method is used to have unique access to all vec parts at once in `extend_from_within`. unsafe fn split_at_spare_mut_with_len( &mut self, ) -> (&mut [T], &mut [MaybeUninit<T>], &mut usize) { </ins> let Range { start: ptr, end: spare_ptr } = self.as_mut_ptr_range(); let spare_ptr = spare_ptr.cast::<MaybeUninit<T>>(); let spare_len = self.buf.capacity() - self.len;

let initialized = slice::from_raw_parts_mut(ptr, self.len); let spare = slice::from_raw_parts_mut(spare_ptr, spare_len); <del> (initialized, spare) </del> <ins> (initialized, spare, &mut self.len) </ins> } } }

impl<T: Clone, A: Allocator> ExtendFromWithinSpec for Vec<T, A> { default unsafe fn spec_extend_from_within(&mut self, src: Range<usize>) { <del> let initialized = { let (this, spare) = self.split_at_spare_mut(); // SAFETY: // - caller guaratees that src is a valid index let to_clone = unsafe { this.get_unchecked(src) }; to_clone.iter().cloned().zip(spare.iter_mut()).map(|(e, s)| s.write(e)).count() }; </del> <ins> // SAFETY: // - len is increased only after initializing elements let (this, spare, len) = unsafe { self.split_at_spare_mut_with_len() }; </ins> // SAFETY: <del> // - elements were just initialized unsafe { let new_len = self.len() + initialized; self.set_len(new_len); } </del> <ins> // - caller guaratees that src is a valid index let to_clone = unsafe { this.get_unchecked(src) }; to_clone .iter() .cloned() .zip(spare.iter_mut()) .map(|(src, dst)| dst.write(src)) // Note: // - Element was just initialized with `MaybeUninit::write`, so it's ok to increace len // - len is increased after each element to prevent leaks (see issue #82533) .for_each(|_| *len += 1); </ins> } }

use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; <ins> use std::sync::atomic::{AtomicU32, Ordering}; </ins> use std::vec::{Drain, IntoIter}; struct DropCounter<'a> {

assert_eq!(v, ["a", "b", "c", "b", "c", "a", "b"]); } <ins> // Regression test for issue #82533 #[test] fn test_extend_from_within_panicing_clone() { struct Panic<'dc> { drop_count: &'dc AtomicU32, aaaaa: bool, } impl Clone for Panic<'_> { fn clone(&self) -> Self { if self.aaaaa { panic!("panic! at the clone"); } Self { ..*self } } } impl Drop for Panic<'_> { fn drop(&mut self) { self.drop_count.fetch_add(1, Ordering::SeqCst); } } let count = core::sync::atomic::AtomicU32::new(0); let mut vec = vec![ Panic { drop_count: &count, aaaaa: false }, Panic { drop_count: &count, aaaaa: true }, Panic { drop_count: &count, aaaaa: false }, ]; // This should clone&append one Panic{..} at the end, and then panic while // cloning second Panic{..}. This means that `Panic::drop` should be called // 4 times (3 for items already in vector, 1 for just appended). // // Previously just appended item was leaked, making drop_count = 3, instead of 4. std::panic::catch_unwind(move || vec.extend_from_within(..)).unwrap_err(); assert_eq!(count.load(Ordering::SeqCst), 4); } </ins>