Layout
First off, we need to come up with the struct layout. A Vec has three parts:a pointer to the allocation, the size of the allocation, and the number ofelements that have been initialized.
Naively, this means we just want this design:
pub struct Vec<T> {
ptr: *mut T,
cap: usize,
len: usize,
}
# fn main() {}
And indeed this would compile. Unfortunately, it would be incorrect. First, thecompiler will give us too strict variance. So a &Vec<&'static str>
couldn’t be used where an &Vec<&'a str>
was expected. More importantly, itwill give incorrect ownership information to the drop checker, as it willconservatively assume we don’t own any values of type T
. See the chapteron ownership and lifetimes for all the details on variance anddrop check.
As we saw in the ownership chapter, we should use Unique<T>
in place of*mut T
when we have a raw pointer to an allocation we own. Unique is unstable,so we’d like to not use it if possible, though.
As a recap, Unique is a wrapper around a raw pointer that declares that:
- We are variant over
T
- We may own a value of type
T
(for drop check) - We are Send/Sync if
T
is Send/Sync - Our pointer is never null (so
Option<Vec<T>>
is null-pointer-optimized)
We can implement all of the above requirements except for the lastone in stable Rust:
use std::marker::PhantomData;
use std::ops::Deref;
use std::mem;
struct Unique<T> {
ptr: *const T, // *const for variance
_marker: PhantomData<T>, // For the drop checker
}
// Deriving Send and Sync is safe because we are the Unique owners
// of this data. It's like Unique<T> is "just" T.
unsafe impl<T: Send> Send for Unique<T> {}
unsafe impl<T: Sync> Sync for Unique<T> {}
impl<T> Unique<T> {
pub fn new(ptr: *mut T) -> Self {
Unique { ptr: ptr, _marker: PhantomData }
}
pub fn as_ptr(&self) -> *mut T {
self.ptr as *mut T
}
}
# fn main() {}
Unfortunately the mechanism for stating that your value is non-zero isunstable and unlikely to be stabilized soon. As such we’re just going totake the hit and use std’s Unique:
#![feature(ptr_internals)]
use std::ptr::{Unique, self};
pub struct Vec<T> {
ptr: Unique<T>,
cap: usize,
len: usize,
}
# fn main() {}
If you don’t care about the null-pointer optimization, then you can use thestable code. However we will be designing the rest of the code around enablingthis optimization. It should be noted that Unique::new
is unsafe to call, becauseputting null
inside of it is Undefined Behavior. Our stable Unique doesn’tneed new
to be unsafe because it doesn’t make any interesting guarantees aboutits contents.