Improve chapter about `Vec<T>` (#381)

Co-authored-by: Daniel Henry-Mantilla <daniel.henry.mantilla@gmail.com>
pull/387/head
nils 2 years ago committed by GitHub
parent 03fbddb1f0
commit ae406aa528
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -28,7 +28,6 @@ impl<T> Vec<T> {
ptr: NonNull::dangling(), ptr: NonNull::dangling(),
len: 0, len: 0,
cap: 0, cap: 0,
_marker: PhantomData,
} }
} }
} }

@ -10,7 +10,6 @@ use std::ptr::{self, NonNull};
struct RawVec<T> { struct RawVec<T> {
ptr: NonNull<T>, ptr: NonNull<T>,
cap: usize, cap: usize,
_marker: PhantomData<T>,
} }
unsafe impl<T: Send> Send for RawVec<T> {} unsafe impl<T: Send> Send for RawVec<T> {}
@ -25,7 +24,6 @@ impl<T> RawVec<T> {
RawVec { RawVec {
ptr: NonNull::dangling(), ptr: NonNull::dangling(),
cap: cap, cap: cap,
_marker: PhantomData,
} }
} }

@ -22,9 +22,11 @@ pub fn insert(&mut self, index: usize, elem: T) {
unsafe { unsafe {
// ptr::copy(src, dest, len): "copy from src to dest len elems" // ptr::copy(src, dest, len): "copy from src to dest len elems"
ptr::copy(self.ptr.as_ptr().add(index), ptr::copy(
self.ptr.as_ptr().add(index + 1), self.ptr.as_ptr().add(index),
self.len - index); self.ptr.as_ptr().add(index + 1),
self.len - index,
);
ptr::write(self.ptr.as_ptr().add(index), elem); ptr::write(self.ptr.as_ptr().add(index), elem);
self.len += 1; self.len += 1;
} }
@ -42,9 +44,11 @@ pub fn remove(&mut self, index: usize) -> T {
unsafe { unsafe {
self.len -= 1; self.len -= 1;
let result = ptr::read(self.ptr.as_ptr().add(index)); let result = ptr::read(self.ptr.as_ptr().add(index));
ptr::copy(self.ptr.as_ptr().add(index + 1), ptr::copy(
self.ptr.as_ptr().add(index), self.ptr.as_ptr().add(index + 1),
self.len - index); self.ptr.as_ptr().add(index),
self.len - index,
);
result result
} }
} }

@ -49,7 +49,6 @@ pub struct IntoIter<T> {
cap: usize, cap: usize,
start: *const T, start: *const T,
end: *const T, end: *const T,
_marker: PhantomData<T>,
} }
``` ```
@ -61,13 +60,13 @@ impl<T> IntoIterator for Vec<T> {
type Item = T; type Item = T;
type IntoIter = IntoIter<T>; type IntoIter = IntoIter<T>;
fn into_iter(self) -> IntoIter<T> { fn into_iter(self) -> IntoIter<T> {
// Can't destructure Vec since it's Drop
let ptr = self.ptr;
let cap = self.cap;
let len = self.len;
// Make sure not to drop Vec since that would free the buffer // Make sure not to drop Vec since that would free the buffer
mem::forget(self); let vec = ManuallyDrop::new(self);
// Can't destructure Vec since it's Drop
let ptr = vec.ptr;
let cap = vec.cap;
let len = vec.len;
unsafe { unsafe {
IntoIter { IntoIter {
@ -80,7 +79,6 @@ impl<T> IntoIterator for Vec<T> {
} else { } else {
ptr.as_ptr().add(len) ptr.as_ptr().add(len)
}, },
_marker: PhantomData,
} }
} }
} }

@ -15,13 +15,10 @@ pub struct Vec<T> {
} }
``` ```
And indeed this would compile. Unfortunately, it would be incorrect. First, the And indeed this would compile. Unfortunately, it would be too strict. The
compiler will give us too strict variance. So a `&Vec<&'static str>` compiler will give us too strict variance. So a `&Vec<&'static str>`
couldn't be used where an `&Vec<&'a str>` was expected. More importantly, it couldn't be used where a `&Vec<&'a str>` was expected. See [the chapter
will give incorrect ownership information to the drop checker, as it will on ownership and lifetimes][ownership] for all the details on variance.
conservatively assume we don't own any values of type `T`. See [the chapter
on ownership and lifetimes][ownership] for all the details on variance and
drop check.
As we saw in the ownership chapter, the standard library uses `Unique<T>` in place of As we saw in the ownership chapter, the standard library uses `Unique<T>` in place of
`*mut T` when it has a raw pointer to an allocation that it owns. Unique is unstable, `*mut T` when it has a raw pointer to an allocation that it owns. Unique is unstable,
@ -30,16 +27,16 @@ 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: As a recap, Unique is a wrapper around a raw pointer that declares that:
* We are covariant over `T` * We are covariant over `T`
* We may own a value of type `T` (for drop check) * 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 * We are Send/Sync if `T` is Send/Sync
* Our pointer is never null (so `Option<Vec<T>>` is null-pointer-optimized) * 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, instead We can implement all of the above requirements in stable Rust. To do this, instead
of using `Unique<T>` we will use [`NonNull<T>`][NonNull], another wrapper around a of using `Unique<T>` we will use [`NonNull<T>`][NonNull], another wrapper around a
raw pointer, which gives us two of the above properties, namely it is covariant raw pointer, which gives us two of the above properties, namely it is covariant
over `T` and is declared to never be null. By adding a `PhantomData<T>` (for drop over `T` and is declared to never be null. By implementing Send/Sync if `T` is,
check) and implementing Send/Sync if `T` is, we get the same results as using we get the same results as using `Unique<T>`:
`Unique<T>`:
```rust ```rust
use std::ptr::NonNull; use std::ptr::NonNull;
@ -49,7 +46,6 @@ pub struct Vec<T> {
ptr: NonNull<T>, ptr: NonNull<T>,
cap: usize, cap: usize,
len: usize, len: usize,
_marker: PhantomData<T>,
} }
unsafe impl<T: Send> Send for Vec<T> {} unsafe impl<T: Send> Send for Vec<T> {}
@ -58,4 +54,5 @@ unsafe impl<T: Sync> Sync for Vec<T> {}
``` ```
[ownership]: ../ownership.html [ownership]: ../ownership.html
[phantom-data]: ../phantom-data.md
[NonNull]: ../../std/ptr/struct.NonNull.html [NonNull]: ../../std/ptr/struct.NonNull.html

@ -13,7 +13,6 @@ allocating, growing, and freeing:
struct RawVec<T> { struct RawVec<T> {
ptr: NonNull<T>, ptr: NonNull<T>,
cap: usize, cap: usize,
_marker: PhantomData<T>,
} }
unsafe impl<T: Send> Send for RawVec<T> {} unsafe impl<T: Send> Send for RawVec<T> {}
@ -25,7 +24,6 @@ impl<T> RawVec<T> {
RawVec { RawVec {
ptr: NonNull::dangling(), ptr: NonNull::dangling(),
cap: 0, cap: 0,
_marker: PhantomData,
} }
} }

@ -33,14 +33,13 @@ method of `RawVec`.
```rust,ignore ```rust,ignore
impl<T> RawVec<T> { impl<T> RawVec<T> {
fn new() -> Self { fn new() -> Self {
// !0 is usize::MAX. This branch should be stripped at compile time. // This branch should be stripped at compile time.
let cap = if mem::size_of::<T>() == 0 { !0 } else { 0 }; let cap = if mem::size_of::<T>() == 0 { usize::MAX } else { 0 };
// `NonNull::dangling()` doubles as "unallocated" and "zero-sized allocation" // `NonNull::dangling()` doubles as "unallocated" and "zero-sized allocation"
RawVec { RawVec {
ptr: NonNull::dangling(), ptr: NonNull::dangling(),
cap: cap, cap: cap,
_marker: PhantomData,
} }
} }

Loading…
Cancel
Save