RawVec

我们实际上在这里达到了一个有趣的状态:我们在 Vec 和 IntoIter 中重复了指定缓冲区和释放其内存的逻辑。现在我们已经实现了它,并且确定了实际的逻辑重复,这是一个进行一些逻辑压缩的好时机。

我们将抽象出(ptr, cap)对,并为它们编写分配、增长和释放的逻辑:

  1. struct RawVec<T> {
  2. ptr: NonNull<T>,
  3. cap: usize,
  4. _marker: PhantomData<T>,
  5. }
  6. unsafe impl<T: Send> Send for RawVec<T> {}
  7. unsafe impl<T: Sync> Sync for RawVec<T> {}
  8. impl<T> RawVec<T> {
  9. fn new() -> Self {
  10. assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support");
  11. RawVec {
  12. ptr: NonNull::dangling(),
  13. cap: 0,
  14. _marker: PhantomData,
  15. }
  16. }
  17. fn grow(&mut self) {
  18. let (new_cap, new_layout) = if self.cap == 0 {
  19. (1, Layout::array::<T>(1).unwrap())
  20. } else {
  21. // 保证新申请的内存没有超出 `isize::MAX` 字节
  22. let new_cap = 2 * self.cap;
  23. // `Layout::array` 会检查申请的空间是否小于等于 usize::MAX,
  24. // 但是因为 old_layout.size() <= isize::MAX,
  25. // 所以这里的 unwrap 永远不可能失败
  26. let new_layout = Layout::array::<T>(new_cap).unwrap();
  27. (new_cap, new_layout)
  28. };
  29. // 保证新申请的内存没有超出 `isize::MAX` 字节
  30. assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large");
  31. let new_ptr = if self.cap == 0 {
  32. unsafe { alloc::alloc(new_layout) }
  33. } else {
  34. let old_layout = Layout::array::<T>(self.cap).unwrap();
  35. let old_ptr = self.ptr.as_ptr() as *mut u8;
  36. unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) }
  37. };
  38. // 如果分配失败,`new_ptr` 就会成为空指针,我们需要对应 abort 的操作
  39. self.ptr = match NonNull::new(new_ptr as *mut T) {
  40. Some(p) => p,
  41. None => alloc::handle_alloc_error(new_layout),
  42. };
  43. self.cap = new_cap;
  44. }
  45. }
  46. impl<T> Drop for RawVec<T> {
  47. fn drop(&mut self) {
  48. if self.cap != 0 {
  49. let layout = Layout::array::<T>(self.cap).unwrap();
  50. unsafe {
  51. alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
  52. }
  53. }
  54. }
  55. }

随后,把 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 {
  7. self.buf.ptr.as_ptr()
  8. }
  9. fn cap(&self) -> usize {
  10. self.buf.cap
  11. }
  12. pub fn new() -> Self {
  13. Vec {
  14. buf: RawVec::new(),
  15. len: 0,
  16. }
  17. }
  18. // push/pop/insert/remove 这些操作做了小小的改变,如下所示:
  19. // * `self.ptr.as_ptr() -> self.ptr()`
  20. // * `self.cap -> self.cap()`
  21. // * `self.grow() -> self.buf.grow()`
  22. }
  23. impl<T> Drop for Vec<T> {
  24. fn drop(&mut self) {
  25. while let Some(_) = self.pop() {}
  26. // RawVec 来负责释放内存
  27. }
  28. }

最后,我们可以把 IntoIter 改得相当简单:

  1. pub 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. // 我们只需要确保 Vec 中所有元素都被读取了,
  10. // 在这之后这些元素会被自动清理
  11. for _ in &mut *self {}
  12. }
  13. }
  14. impl<T> IntoIterator for Vec<T> {
  15. type Item = T;
  16. type IntoIter = IntoIter<T>;
  17. fn into_iter(self) -> IntoIter<T> {
  18. unsafe {
  19. // 需要使用 ptr::read 非安全地把 buf 移出,因为它没有实现 Copy,
  20. // 而且 Vec 实现了 Drop Trait (因此我们不能销毁它)
  21. let buf = ptr::read(&self.buf);
  22. let len = self.len;
  23. mem::forget(self);
  24. IntoIter {
  25. start: buf.ptr.as_ptr(),
  26. end: if buf.cap == 0 {
  27. // 不能通过这个指针获取偏移,除非已经分配了内存
  28. buf.ptr.as_ptr()
  29. } else {
  30. buf.ptr.as_ptr().add(len)
  31. },
  32. _buf: buf,
  33. }
  34. }
  35. }
  36. }

是不是好多了!