|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
# Allocating Memory
|
|
|
|
|
|
|
|
|
|
Using NonNull throws a wrench in an important feature of Vec (and indeed all of
|
|
|
|
|
Using `NonNull` throws a wrench in an important feature of Vec (and indeed all of
|
|
|
|
|
the std collections): creating an empty Vec doesn't actually allocate at all. This
|
|
|
|
|
is not the same as allocating a zero-sized memory block, which is not allowed by
|
|
|
|
|
the global allocator (it results in undefined behavior!). So if we can't allocate,
|
|
|
|
@ -10,7 +10,7 @@ just put some other garbage in there!
|
|
|
|
|
This is perfectly fine because we already have `cap == 0` as our sentinel for no
|
|
|
|
|
allocation. We don't even need to handle it specially in almost any code because
|
|
|
|
|
we usually need to check if `cap > len` or `len > 0` anyway. The recommended
|
|
|
|
|
Rust value to put here is `mem::align_of::<T>()`. NonNull provides a convenience
|
|
|
|
|
Rust value to put here is `mem::align_of::<T>()`. `NonNull` provides a convenience
|
|
|
|
|
for this: `NonNull::dangling()`. There are quite a few places where we'll
|
|
|
|
|
want to use `dangling` because there's no real allocation to talk about but
|
|
|
|
|
`null` would make the compiler do bad things.
|
|
|
|
@ -27,7 +27,7 @@ impl<T> Vec<T> {
|
|
|
|
|
ptr: NonNull::dangling(),
|
|
|
|
|
len: 0,
|
|
|
|
|
cap: 0,
|
|
|
|
|
_marker: PhantomData
|
|
|
|
|
_marker: PhantomData,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -173,7 +173,7 @@ impl<T> Vec<T> {
|
|
|
|
|
// This can't overflow since self.cap <= isize::MAX.
|
|
|
|
|
let new_cap = 2 * self.cap;
|
|
|
|
|
|
|
|
|
|
// Layout::array checks that the number of bytes is <= usize::MAX,
|
|
|
|
|
// `Layout::array` checks that the number of bytes is <= usize::MAX,
|
|
|
|
|
// but this is redundant since old_layout.size() <= isize::MAX,
|
|
|
|
|
// so the `unwrap` should never fail.
|
|
|
|
|
let new_layout = Layout::array::<T>(new_cap).unwrap();
|
|
|
|
|