]> git.proxmox.com Git - rustc.git/blame - src/doc/nomicon/src/vec/vec-raw.md
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / src / doc / nomicon / src / vec / vec-raw.md
CommitLineData
8bb4bdeb 1# RawVec
c1a9b12d
SL
2
3We've actually reached an interesting situation here: we've duplicated the logic
4for specifying a buffer and freeing its memory in Vec and IntoIter. Now that
5we've implemented it and identified *actual* logic duplication, this is a good
6time to perform some logic compression.
7
8We're going to abstract out the `(ptr, cap)` pair and give them the logic for
9allocating, growing, and freeing:
10
136023e0 11<!-- ignore: simplified code -->
c1a9b12d
SL
12```rust,ignore
13struct RawVec<T> {
17df50a5 14 ptr: NonNull<T>,
c1a9b12d 15 cap: usize,
17df50a5 16 _marker: PhantomData<T>,
c1a9b12d
SL
17}
18
17df50a5
XL
19unsafe impl<T: Send> Send for RawVec<T> {}
20unsafe impl<T: Sync> Sync for RawVec<T> {}
21
c1a9b12d
SL
22impl<T> RawVec<T> {
23 fn new() -> Self {
17df50a5
XL
24 assert!(mem::size_of::<T>() != 0, "TODO: implement ZST support");
25 RawVec {
26 ptr: NonNull::dangling(),
27 cap: 0,
28 _marker: PhantomData,
29 }
c1a9b12d
SL
30 }
31
c1a9b12d 32 fn grow(&mut self) {
17df50a5
XL
33 let (new_cap, new_layout) = if self.cap == 0 {
34 (1, Layout::array::<T>(1).unwrap())
35 } else {
36 // This can't overflow because we ensure self.cap <= isize::MAX.
37 let new_cap = 2 * self.cap;
38
39 // Layout::array checks that the number of bytes is <= usize::MAX,
40 // but this is redundant since old_layout.size() <= isize::MAX,
41 // so the `unwrap` should never fail.
42 let new_layout = Layout::array::<T>(new_cap).unwrap();
43 (new_cap, new_layout)
44 };
45
46 // Ensure that the new allocation doesn't exceed `isize::MAX` bytes.
47 assert!(new_layout.size() <= isize::MAX as usize, "Allocation too large");
48
49 let new_ptr = if self.cap == 0 {
50 unsafe { alloc::alloc(new_layout) }
51 } else {
52 let old_layout = Layout::array::<T>(self.cap).unwrap();
53 let old_ptr = self.ptr.as_ptr() as *mut u8;
54 unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) }
55 };
56
57 // If allocation fails, `new_ptr` will be null, in which case we abort.
58 self.ptr = match NonNull::new(new_ptr as *mut T) {
59 Some(p) => p,
60 None => alloc::handle_alloc_error(new_layout),
61 };
62 self.cap = new_cap;
c1a9b12d
SL
63 }
64}
65
c1a9b12d
SL
66impl<T> Drop for RawVec<T> {
67 fn drop(&mut self) {
68 if self.cap != 0 {
17df50a5 69 let layout = Layout::array::<T>(self.cap).unwrap();
c1a9b12d 70 unsafe {
17df50a5 71 alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout);
c1a9b12d
SL
72 }
73 }
74 }
75}
76```
77
78And change Vec as follows:
79
136023e0 80<!-- ignore: simplified code -->
c1a9b12d
SL
81```rust,ignore
82pub struct Vec<T> {
83 buf: RawVec<T>,
84 len: usize,
85}
86
87impl<T> Vec<T> {
17df50a5
XL
88 fn ptr(&self) -> *mut T {
89 self.buf.ptr.as_ptr()
90 }
c1a9b12d 91
17df50a5
XL
92 fn cap(&self) -> usize {
93 self.buf.cap
94 }
c1a9b12d
SL
95
96 pub fn new() -> Self {
17df50a5
XL
97 Vec {
98 buf: RawVec::new(),
99 len: 0,
100 }
c1a9b12d
SL
101 }
102
103 // push/pop/insert/remove largely unchanged:
5869c6ff 104 // * `self.ptr.as_ptr() -> self.ptr()`
c1a9b12d 105 // * `self.cap -> self.cap()`
17df50a5 106 // * `self.grow() -> self.buf.grow()`
c1a9b12d
SL
107}
108
109impl<T> Drop for Vec<T> {
110 fn drop(&mut self) {
111 while let Some(_) = self.pop() {}
112 // deallocation is handled by RawVec
113 }
114}
115```
116
117And finally we can really simplify IntoIter:
118
136023e0 119<!-- ignore: simplified code -->
c1a9b12d 120```rust,ignore
5869c6ff 121pub struct IntoIter<T> {
c1a9b12d
SL
122 _buf: RawVec<T>, // we don't actually care about this. Just need it to live.
123 start: *const T,
124 end: *const T,
125}
126
127// next and next_back literally unchanged since they never referred to the buf
128
129impl<T> Drop for IntoIter<T> {
130 fn drop(&mut self) {
131 // only need to ensure all our elements are read;
132 // buffer will clean itself up afterwards.
133 for _ in &mut *self {}
134 }
135}
136
137impl<T> Vec<T> {
138 pub fn into_iter(self) -> IntoIter<T> {
139 unsafe {
140 // need to use ptr::read to unsafely move the buf out since it's
141 // not Copy, and Vec implements Drop (so we can't destructure it).
142 let buf = ptr::read(&self.buf);
143 let len = self.len;
144 mem::forget(self);
145
146 IntoIter {
5869c6ff 147 start: buf.ptr.as_ptr(),
94222f64
XL
148 end: if buf.cap == 0 {
149 // can't offset off of a pointer unless it's part of an allocation
150 buf.ptr.as_ptr()
151 } else {
152 buf.ptr.as_ptr().add(len)
153 },
c1a9b12d
SL
154 _buf: buf,
155 }
156 }
157 }
158}
159```
160
161Much better.