blob: b9f30bf388051312f990b489953789daf35d9500 [file] [view] [edit]
# 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 of
elements that have been initialized.
Naively, this means we just want this design:
<!-- ignore: simplified code -->
```rust,ignore
pub struct Vec<T> {
ptr: *mut T,
cap: usize,
len: usize,
}
```
And indeed this would compile. Unfortunately, it would be too strict. The
compiler will give us too strict variance. So a `&Vec<&'static str>`
couldn't be used where a `&Vec<&'a str>` was expected. See [the chapter
on ownership and lifetimes][ownership] for all the details on variance.
As we saw in the ownership chapter, when the standard library has a raw pointer
to an allocation that it owns, it wants that pointer to declare more than a bare
`*mut T` does:
* We are covariant over `T`
* We may own a value of type `T` (this is not relevant for our example here, but see
[the chapter on PhantomData][phantom-data] on why the real `std::vec::Vec<T>` needs this)
* 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 in stable Rust. To do this, we will
use [`NonNull<T>`][NonNull], a wrapper around a raw pointer which gives us two of
the above properties, namely it is covariant over `T` and is declared to never be
null. By implementing Send/Sync if `T` is, we get the rest of what we need:
```rust
use std::ptr::NonNull;
pub struct Vec<T> {
ptr: NonNull<T>,
cap: usize,
len: usize,
}
unsafe impl<T: Send> Send for Vec<T> {}
unsafe impl<T: Sync> Sync for Vec<T> {}
# fn main() {}
```
[ownership]: ../ownership.html
[phantom-data]: ../phantom-data.md
[NonNull]: ../../std/ptr/struct.NonNull.html