前面我们已经实现了Vec的迭代器功能,可以循环访问Vec中的元素。但是你们有没有发现有一个有意思的情况了?那就是我们把初始化缓存和释放内存的逻辑在 Vec 和 IntoIter 里面一模一样地写了两次。现在我们已经实现了功能,而且发现了逻辑的重复,是时候对代码做一些压缩了。因而我们就要抽象出(ptr,cap)并赋予他们分配,扩容释放的逻辑。
RawVec
struct RawVec<T> {
ptr: Unique<T>,
cap: usize,
}
impl<T> RawVec<T> {
fn new() -> Self {
assert!(mem::size_of::<T>() != 0, "TODO:实现零尺寸类型的支持");
RawVec { ptr: Unique::empty(), cap: 0 }
}
// 与Vec一样
fn grow(&mut self) {
unsafe {
let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>();
let (new_cap, ptr) = if self.cap == 0 {
let ptr = heap::allocate(elem_size, align);
(1, ptr)
} else {
let new_cap = 2 * self.cap;
let ptr = heap::reallocate(self.ptr.as_ptr() as *mut _,
self.cap * elem_size,
new_cap * elem_size,
align);
(new_cap, ptr)
};
// 如果分配或再分配失败,我们会得到null
if ptr.is_null() { oom() }
self.ptr = Unique::new(ptr as *mut _);
self.cap = new_cap;
}
}
}
impl<T> Drop for RawVec<T> {
fn drop(&mut self) {
if self.cap != 0 {
let align = mem::align_of::<T>();
let elem_size = mem::size_of::<T>();
let num_bytes = elem_size * self.cap;
unsafe {
heap::deallocate(self.ptr.as_mut() as *mut _, num_bytes, align);
}
}
}
}
我们需要改造下我们之前的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基本没变,只改变了:
// self.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() {}
// 释放空间由RawVec负责
}
}
然后我们简化迭代器:
struct IntoIter<T> {
_buf: RawVec<T>, // 我们并不关心这个,只是需要它们保持分配空间不被销毁
start: *const T,
end: *const T,
}
// next和next_back保持不变,因为它们并没有用到buf
impl<T> Drop for IntoIter<T> {
fn drop(&mut self) {
// 只需要保证所有的元素都被读到了
// 缓存会在随后自己清理自己
for _ in &mut *self {}
}
}
impl<T> Vec<T> {
pub fn into_iter(self) -> IntoIter<T> {
unsafe {
// 需要使用ptr::read非安全地把buf移出,因为它不是Copy,
// 而且Vec实现了Drop(所以我们不能销毁它)
let buf = ptr::read(&self.buf);
let len = self.len;
mem::forget(self);
IntoIter {
start: *buf.ptr,
end: buf.ptr.offset(len as isize),
_buf: buf,
}
}
}
}
Drain
这样就看起来好多了嘛!!!
你以为这就结束了吗,不不,精彩的还在后面让我们继续探索Drain对Vec的影响,本质上和迭代器一样都是迭代Vec中的元素,但是与迭代器不同的是它并不获取 Vec 的值,而是借用 Vec 并且不改变它的分配空间。
不要逼逼,展示代码:
use std::marker::PhantomData;
struct Drain<'a, T: 'a> {
// 这里需要限制生命周期。我们使用&'a mut Vec<T>,因为这就是语义上我们包含的东西。
// 我们只调用pop()和remove(0)
vec: PhantomData<&'a mut Vec<T>>,
start: *const T,
end: *const T,
}
impl<'a, T> Iterator for Drain<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.start == self.end {
None
等一下,难道你眼瞎嘛,IntoIter 和 Drain 有着完全一样的结构,那不得提取出来,本着代码优雅,复用原则也得整一波不是。再看抽象后的代码:
struct RawValIter<T> {
start: *const T,
end: *const T,
}
impl<T> RawValIter<T> {
// 构建它是非安全的,因为它没有关联的生命周期。
unsafe fn new(slice: &[T]) -> Self {
RawValIter {
start: slice.as_ptr(),
end: if slice.len() == 0 {
// 如果len == 0,说明没有真的分配内存。这时需要避免offset,
// 因为那会给LLVM的GEP提供错误的信息
slice.as_ptr()
} else {
slice.as_ptr().offset(slice.len() as isize)
}
}
}
}
// Iterator和DoubleEndedIterator的实现与IntoIter完全一样。
这样就感觉舒坦多了,允许你自我陶醉一会,接着我们要把IntoIter 改造下,代码如下:
pub struct IntoIter<T> {
_buf: RawVec<T>, // 我们并不关心这个,只是需要它们保持分配空间不被销毁
iter: RawValIter<T>,
}
impl<T> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl<T> DoubleEndedIterator for IntoIter<T> {
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
}
impl<T> Drop for IntoIter<T> {
fn drop(&mut self) {
for _ in &mut self.iter {}
}
}
impl<T> Vec<T> {
pub fn into_iter(self) -> IntoIter<T> {
unsafe {
let iter = RawValIter::new(&self);
let buf = ptr::read(&self.buf);
mem::forget(self);
IntoIter {
iter: iter,
_buf: buf,
}
}
}
}
到了这里Drain不就变得很简单了嘛,各位大哥请看下面代码:
use std::marker::PhantomData;
pub struct Drain<'a, T: 'a> {
vec: PhantomData<&'a mut Vec<T>>,
iter: RawValIter<T>,
}
impl<'a, T> Iterator for Drain<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> { self.iter.next() }
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl<'a, T> DoubleEndedIterator for Drain<'a, T> {
fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
}
impl<'a, T> Drop for Drain<'a, T> {
fn drop(&mut self) {
for _ in &mut self.iter {}
}
}
impl<T> Vec<T> {
pub fn drain(&mut self) -> Drain<T> {
unsafe {
let iter = RawValIter::new(&self);
// 这一步是为了mem::forget的安全。如果Drain被forget,我们会泄露整个Vec的内容
// 同时,既然我们无论如何都会做这一步,为什么不现在做呢?
self.len = 0;
Drain {
iter: iter,
vec: PhantomData,
}
}
}
}
好了,各位大哥我们对Vec的进一步优化就先到这里,烦请各位大哥给小弟一个关注,小弟我不胜感激。