原文链接:https://doc.rust-lang.org/nomicon/vec-raw.html

RawVec

我们遇到了一个很有意思的情况:我们把初始化缓存和释放内存的逻辑在Vec和IntoIter里面一模一样地写了两次。现在我们已经实现了功能,而且发现了逻辑的重复,是时候对代码做一些压缩了。

我们要抽象出(ptr, cap),并赋予它们分配、扩容和释放的逻辑:

  1. struct RawVec<T> {
  2. ptr: Unique<T>,
  3. cap: usize,
  4. }
  5. impl<T> RawVec<T> {
  6. fn new() -> Self {
  7. assert!(mem::size_of::<T>() != 0, "TODO:实现零尺寸类型的支持");
  8. RawVec { ptr: Unique::empty(), cap: 0 }
  9. }
  10. // 与Vec一样
  11. fn grow(&mut self) {
  12. unsafe {
  13. let align = mem::align_of::<T>();
  14. let elem_size = mem::size_of::<T>();
  15. let (new_cap, ptr) = if self.cap == 0 {
  16. let ptr = heap::allocate(elem_size, align);
  17. (1, ptr)
  18. } else {
  19. let new_cap = 2 * self.cap;
  20. let ptr = heap::reallocate(self.ptr.as_ptr() as *mut _,
  21. self.cap * elem_size,
  22. new_cap * elem_size,
  23. align);
  24. (new_cap, ptr)
  25. };
  26. // 如果分配或再分配失败,我们会得到null
  27. if ptr.is_null() { oom() }
  28. self.ptr = Unique::new(ptr as *mut _);
  29. self.cap = new_cap;
  30. }
  31. }
  32. }
  33. impl<T> Drop for RawVec<T> {
  34. fn drop(&mut self) {
  35. if self.cap != 0 {
  36. let align = mem::align_of::<T>();
  37. let elem_size = mem::size_of::<T>();
  38. let num_bytes = elem_size * self.cap;
  39. unsafe {
  40. heap::deallocate(self.ptr.as_mut() as *mut _, num_bytes, align);
  41. }
  42. }
  43. }
  44. }

然后像下面这样改写Vec:

  1. pub struct Vec<T> {
  2. buf: RawVec<T>,
  3. len: usize,
  4. }
  5. impl<T> Vec<T> {
  6. fn ptr(&self) -> *mut T { self.buf.ptr.as_ptr() }
  7. fn cap(&self) -> usize { self.buf.cap }
  8. pub fn new() -> Self {
  9. Vec { buf: RawVec::new(), len: 0 }
  10. }
  11. // push/pop/insert/remove基本没变,只改变了:
  12. // self.ptr -> self.ptr()
  13. // self.cap -> self.cap()
  14. // self.grow -> self.buf.grow()
  15. }
  16. impl<T> Drop for Vec<T> {
  17. fn drop(&mut self) {
  18. while let Some(_) = self.pop() {}
  19. // 释放空间由RawVec负责
  20. }
  21. }

最后我们可以简化IntoIter:

  1. struct IntoIter<T> {
  2. _buf: RawVec<T>, // 我们并不关心这个,只是需要它们保持分配空间不被销毁
  3. start: *const T,
  4. end: *const T,
  5. }
  6. // next和next_back保持不变,因为它们并没有用到buf
  7. impl<T> Drop for IntoIter<T> {
  8. fn drop(&mut self) {
  9. // 只需要保证所有的元素都被读到了
  10. // 缓存会在随后自己清理自己
  11. for _ in &mut *self {}
  12. }
  13. }
  14. impl<T> Vec<T> {
  15. pub fn into_iter(self) -> IntoIter<T> {
  16. unsafe {
  17. // 需要使用ptr::read非安全地把buf移出,因为它不是Copy,
  18. // 而且Vec实现了Drop(所以我们不能销毁它)
  19. let buf = ptr::read(&self.buf);
  20. let len = self.len;
  21. mem::forget(self);
  22. IntoIter {
  23. start: *buf.ptr,
  24. end: buf.ptr.offset(len as isize),
  25. _buf: buf,
  26. }
  27. }
  28. }
  29. }

现在看起来好多了。