size()

The procedure for size is simple. Load the vector's size from the Descriptor, then subtract one if there is a pending write.

It seems like so long ago that we went over The Algorithm, but recall that when we perform a push, we swap in a Descriptor, then call complete_write. This means that when we do a write, the increase in size is reflected in the vector's state before the write actually happens. If there is still a WriteDescriptor contained in the Descriptor, that means the size stored in the Descriptor is one greater than the actual size of the vector, because complete_write replaces the WriteDescriptor with None when it executes the write.

Here is the code:

#![allow(unused)]
fn main() {
pub fn size(&self) -> usize {
    // # Safety
    // The pointers are safe to dereference because they all came from `Box::into_raw`
    // and point to valid objects

    let desc = unsafe { &*self.descriptor.load(Ordering::Acquire) };
    let size = desc.size;

    // If there is a pending descriptor, we subtract one from the size because
    // `push` increments the size, swaps the new descriptor in, and _then_ writes
    // the value. Therefore the size is one greater because the write hasn't happened
    // yet
    match unsafe { &*desc.pending.load(Ordering::Acquire) } {
        Some(_) => size - 1,
        None => size,
    }
}
}

There have been many momentous moments throughout the book: understanding the algorithm, finishing push, and finally, completing the vector's public API. When I was writing the code, this moment felt huge, and I jumped up and down after pushing 10 elements onto the vector, poping 10 times, and running assert_eq!(sv.size(), 0); withough crashing.

Let's run some tests (more fun than you might think)!