about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSabrinaJewson <sejewson@gmail.com>2025-08-12 05:12:34 +0100
committerSabrinaJewson <sejewson@gmail.com>2025-08-12 05:12:34 +0100
commit1ce4b370e44fbac89b75438c8e75b26bff18d496 (patch)
tree92031905767261e359c5be955ab1e1d152f645bb
parent45e2449b2af5cb8e96a60cddafe4b6a49568a937 (diff)
downloadrust-1ce4b370e44fbac89b75438c8e75b26bff18d496.tar.gz
rust-1ce4b370e44fbac89b75438c8e75b26bff18d496.zip
Handle the `capacity == 0` case
-rw-r--r--library/alloc/src/vec/mod.rs61
1 files changed, 34 insertions, 27 deletions
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index e02265dcafc..8001faf9d20 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -51,19 +51,20 @@
 //!
 //! # Memory layout
 //!
-//! For non-zero-sized types, [`Vec`] uses the [`Global`] allocator for its allocation. It is
-//! valid to convert both ways between a [`Vec`] and a raw pointer allocated with the [`Global`]
-//! allocator, provided that the [`Layout`] used with the allocator is correct for a sequence of
-//! `capacity` elements of the type, and the first `len` values pointed to by the raw pointer are
-//! valid. More precisely, a `ptr: *mut T` that has been allocated with the [`Global`] allocator
-//! with [`Layout::array::<T>(capacity)`][Layout::array] may be converted into a vec using
-//! [`Vec::<T>::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts).
-//! Conversely, the memory backing a `value: *mut T` obtained from [`Vec::<T>::as_mut_ptr`] may be
-//! deallocated using the [`Global`] allocator with the same layout.
+//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`]
+//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw
+//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the
+//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len`
+//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been
+//! allocated with the [`Global`] allocator with [`Layout::array::<T>(capacity)`][Layout::array] may
+//! be converted into a vec using
+//! [`Vec::<T>::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory
+//! backing a `value: *mut T` obtained from [`Vec::<T>::as_mut_ptr`] may be deallocated using the
+//! [`Global`] allocator with the same layout.
 //!
-//! For zero-sized types (ZSTs), the `Vec` pointer must be non-null and sufficiently aligned.
-//! The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be used is to use
-//! [`ptr::NonNull::dangling`].
+//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null
+//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be
+//! used is to use [`ptr::NonNull::dangling`].
 //!
 //! [`push`]: Vec::push
 //! [`ptr::NonNull::dangling`]: NonNull::dangling
@@ -542,18 +543,23 @@ impl<T> Vec<T> {
     /// This is highly unsafe, due to the number of invariants that aren't
     /// checked:
     ///
-    /// * `ptr` must have been allocated using the global allocator, such as via
-    ///   the [`alloc::alloc`] function.
-    /// * `T` needs to have the same alignment as what `ptr` was allocated with.
+    /// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have
+    ///   been allocated using the global allocator, such as via the [`alloc::alloc`]
+    ///   function. If `T` is a zero-sized type or the capacity is zero, `ptr` need
+    ///   only be non-null and aligned.
+    /// * `T` needs to have the same alignment as what `ptr` was allocated with,
+    ///   if the pointer is required to be allocated.
     ///   (`T` having a less strict alignment is not sufficient, the alignment really
     ///   needs to be equal to satisfy the [`dealloc`] requirement that memory must be
     ///   allocated and deallocated with the same layout.)
-    /// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs
-    ///   to be the same size as the pointer was allocated with. (Because similar to
-    ///   alignment, [`dealloc`] must be called with the same layout `size`.)
+    /// * The size of `T` times the `capacity` (ie. the allocated size in bytes), if
+    ///   nonzero, needs to be the same size as the pointer was allocated with.
+    ///   (Because similar to alignment, [`dealloc`] must be called with the same
+    ///   layout `size`.)
     /// * `length` needs to be less than or equal to `capacity`.
     /// * The first `length` values must be properly initialized values of type `T`.
-    /// * `capacity` needs to be the capacity that the pointer was allocated with.
+    /// * `capacity` needs to be the capacity that the pointer was allocated with,
+    ///   if the pointer is required to be allocated.
     /// * The allocated size in bytes must be no larger than `isize::MAX`.
     ///   See the safety documentation of [`pointer::offset`].
     ///
@@ -792,9 +798,10 @@ impl<T> Vec<T> {
     /// memory previously managed by the `Vec`. Most often, one does
     /// this by converting the raw pointer, length, and capacity back
     /// into a `Vec` with the [`from_raw_parts`] function; more generally,
-    /// if `T` is non-zero-sized one may use any method that calls
-    /// [`dealloc`] with a layout of `Layout::array::<T>(capacity)`,
-    /// and if `T` is zero-sized nothing needs to be done.
+    /// if `T` is non-zero-sized and the capacity is nonzero, one may use
+    /// any method that calls [`dealloc`] with a layout of
+    /// `Layout::array::<T>(capacity)`; if `T` is zero-sized or the
+    /// capacity is zero, nothing needs to be done.
     ///
     /// [`from_raw_parts`]: Vec::from_raw_parts
     /// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
@@ -1777,11 +1784,11 @@ impl<T, A: Allocator> Vec<T, A> {
     /// may still invalidate this pointer.
     /// See the second example below for how this guarantee can be used.
     ///
-    /// The method also guarantees that, as long as `T` is not zero-sized, the pointer may be
-    /// passed into [`dealloc`] with a layout of `Layout::array::<T>(capacity)` in order to
-    /// deallocate the backing memory. If this is done, be careful not to run the destructor
-    /// of the `Vec`, as dropping it will result in double-frees. Wrapping the `Vec` in a
-    /// [`ManuallyDrop`] is the typical way to achieve this.
+    /// The method also guarantees that, as long as `T` is not zero-sized and the capacity is
+    /// nonzero, the pointer may be passed into [`dealloc`] with a layout of
+    /// `Layout::array::<T>(capacity)` in order to deallocate the backing memory. If this is done,
+    /// be careful not to run the destructor of the `Vec`, as dropping it will result in
+    /// double-frees. Wrapping the `Vec` in a [`ManuallyDrop`] is the typical way to achieve this.
     ///
     /// # Examples
     ///