]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # Push and Pop |
c1a9b12d SL |
2 | |
3 | Alright. We can initialize. We can allocate. Let's actually implement some | |
4 | functionality! Let's start with `push`. All it needs to do is check if we're | |
5 | full to grow, unconditionally write to the next index, and then increment our | |
6 | length. | |
7 | ||
8 | To do the write we have to be careful not to evaluate the memory we want to write | |
9 | to. At worst, it's truly uninitialized memory from the allocator. At best it's the | |
10 | bits of some old value we popped off. Either way, we can't just index to the memory | |
11 | and dereference it, because that will evaluate the memory as a valid instance of | |
12 | T. Worse, `foo[idx] = x` will try to call `drop` on the old value of `foo[idx]`! | |
13 | ||
14 | The correct way to do this is with `ptr::write`, which just blindly overwrites the | |
15 | target address with the bits of the value we provide. No evaluation involved. | |
16 | ||
17 | For `push`, if the old len (before push was called) is 0, then we want to write | |
18 | to the 0th index. So we should offset by the old len. | |
19 | ||
20 | ```rust,ignore | |
21 | pub fn push(&mut self, elem: T) { | |
22 | if self.len == self.cap { self.grow(); } | |
23 | ||
24 | unsafe { | |
25 | ptr::write(self.ptr.offset(self.len as isize), elem); | |
26 | } | |
27 | ||
28 | // Can't fail, we'll OOM first. | |
29 | self.len += 1; | |
30 | } | |
31 | ``` | |
32 | ||
33 | Easy! How about `pop`? Although this time the index we want to access is | |
34 | initialized, Rust won't just let us dereference the location of memory to move | |
35 | the value out, because that would leave the memory uninitialized! For this we | |
36 | need `ptr::read`, which just copies out the bits from the target address and | |
b039eaaf | 37 | interprets it as a value of type T. This will leave the memory at this address |
c1a9b12d SL |
38 | logically uninitialized, even though there is in fact a perfectly good instance |
39 | of T there. | |
40 | ||
41 | For `pop`, if the old len is 1, we want to read out of the 0th index. So we | |
42 | should offset by the new len. | |
43 | ||
44 | ```rust,ignore | |
45 | pub fn pop(&mut self) -> Option<T> { | |
46 | if self.len == 0 { | |
47 | None | |
48 | } else { | |
49 | self.len -= 1; | |
50 | unsafe { | |
51 | Some(ptr::read(self.ptr.offset(self.len as isize))) | |
52 | } | |
53 | } | |
54 | } | |
55 | ``` |