RawVec(RawVec)
我们实际上已经达到了一个有趣的情况:我们已经复制了用于指定缓冲区并在Vec和IntoIter中释放其内存的逻辑.现在我们已经实现了它并确定了 实际的(actual) 逻辑复制,现在是执行一些逻辑压缩的好时机.
我们将抽象出(ptr, cap)对并给它们分配,增长和释放的逻辑:
struct RawVec<T> {ptr: NonNull<T>,cap: usize,_marker: PhantomData<T>,}unsafe impl<T: Send> Send for RawVec<T> {}unsafe impl<T: Sync> Sync for RawVec<T> {}impl<T> RawVec<T> {fn new() -> Self {assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support");RawVec {ptr: NonNull::dangling(),cap: 0,_marker: PhantomData,}}fn grow(&mut self) {let (new_cap, new_layout) = if self.cap == 0 {(1, Layout::array::<T>(1).unwrap())} else {// This can't overflow because we ensure self.cap <= isize::MAX.let new_cap = 2 * self.cap;// Layout::array checks that the number of bytes is <= usize::MAX,// but this is redundant since old_layout.size() <= isize::MAX,// so the `unwrap` should never fail.let new_layout = Layout::array::<T>(new_cap).unwrap();(new_cap, new_layout)};// Ensure that the new allocation doesn't exceed `isize::MAX` bytes.assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large");let new_ptr = if self.cap == 0 {unsafe { alloc::alloc(new_layout) }} else {let old_layout = Layout::array::<T>(self.cap).unwrap();let old_ptr = self.ptr.as_ptr() as *mut u8;unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) }};// If allocation fails, `new_ptr` will be null, in which case we abort.self.ptr = match NonNull::new(new_ptr as *mut T) {Some(p) => p,None => alloc::handle_alloc_error(new_layout),};self.cap = new_cap;}}impl<T> Drop for RawVec<T> {fn drop(&mut self) {if self.cap != 0 {let layout = Layout::array::<T>(self.cap).unwrap();unsafe {alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);}}}}
并改变Vec如下:
pub struct Vec<T> {buf: RawVec<T>,len: usize,}impl<T> Vec<T> {fn ptr(&self) -> *mut T {self.buf.ptr.as_ptr()}fn cap(&self) -> usize {self.buf.cap}pub fn new() -> Self {Vec {buf: RawVec::new(),len: 0,}}// push/pop/insert/remove largely unchanged:// * `self.ptr.as_ptr() -> self.ptr()`// * `self.cap -> self.cap()`// * `self.grow() -> self.buf.grow()`}impl<T> Drop for Vec<T> {fn drop(&mut self) {while let Some(_) = self.pop() {}// deallocation is handled by RawVec}}
最后我们可以真正简化IntoIter:
pub struct IntoIter<T> {_buf: RawVec<T>, // we don't actually care about this. Just need it to live.start: *const T,end: *const T,}// next and next_back literally unchanged since they never referred to the bufimpl<T> Drop for IntoIter<T> {fn drop(&mut self) {// only need to ensure all our elements are read;// buffer will clean itself up afterwards.for _ in &mut *self {}}}impl<T> Vec<T> {pub fn into_iter(self) -> IntoIter<T> {unsafe {// need to use ptr::read to unsafely move the buf out since it's// not Copy, and Vec implements Drop (so we can't destructure it).let buf = ptr::read(&self.buf);let len = self.len;mem::forget(self);IntoIter {start: buf.ptr.as_ptr(),end: buf.ptr..as_ptr().add(len),_buf: buf,}}}}
好多了.
