Skip to content

Commit 422b3a2

Browse files
authored
Rollup merge of rust-lang#145260 - SabrinaJewson:vec-allocator-docs, r=dtolnay
Make explicit guarantees about `Vec`’s allocator This commit amends the documentation of `Vec::as_mut_ptr` and `Vec::into_raw_parts` to make it explicit that such calls may be paired with calls to `dealloc` with a suitable layout. This guarantee was effectively already provided by the docs of `Vec::from_raw_parts` mentioning `alloc`. Additionally, we copy-paste and adjust the “Memory layout” section from the documentation of `std::boxed` to `std::vec`. This explains the allocator guarantees in more detail.
2 parents a55cf66 + 1ce4b37 commit 422b3a2

File tree

1 file changed

+61
-11
lines changed

1 file changed

+61
-11
lines changed

library/alloc/src/vec/mod.rs

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,27 @@
4949
//! v[1] = v[1] + 5;
5050
//! ```
5151
//!
52+
//! # Memory layout
53+
//!
54+
//! When the type is non-zero-sized and the capacity is nonzero, [`Vec`] uses the [`Global`]
55+
//! allocator for its allocation. It is valid to convert both ways between such a [`Vec`] and a raw
56+
//! pointer allocated with the [`Global`] allocator, provided that the [`Layout`] used with the
57+
//! allocator is correct for a sequence of `capacity` elements of the type, and the first `len`
58+
//! values pointed to by the raw pointer are valid. More precisely, a `ptr: *mut T` that has been
59+
//! allocated with the [`Global`] allocator with [`Layout::array::<T>(capacity)`][Layout::array] may
60+
//! be converted into a vec using
61+
//! [`Vec::<T>::from_raw_parts(ptr, len, capacity)`](Vec::from_raw_parts). Conversely, the memory
62+
//! backing a `value: *mut T` obtained from [`Vec::<T>::as_mut_ptr`] may be deallocated using the
63+
//! [`Global`] allocator with the same layout.
64+
//!
65+
//! For zero-sized types (ZSTs), or when the capacity is zero, the `Vec` pointer must be non-null
66+
//! and sufficiently aligned. The recommended way to build a `Vec` of ZSTs if [`vec!`] cannot be
67+
//! used is to use [`ptr::NonNull::dangling`].
68+
//!
5269
//! [`push`]: Vec::push
70+
//! [`ptr::NonNull::dangling`]: NonNull::dangling
71+
//! [`Layout`]: crate::alloc::Layout
72+
//! [Layout::array]: crate::alloc::Layout::array
5373
5474
#![stable(feature = "rust1", since = "1.0.0")]
5575

@@ -523,18 +543,23 @@ impl<T> Vec<T> {
523543
/// This is highly unsafe, due to the number of invariants that aren't
524544
/// checked:
525545
///
526-
/// * `ptr` must have been allocated using the global allocator, such as via
527-
/// the [`alloc::alloc`] function.
528-
/// * `T` needs to have the same alignment as what `ptr` was allocated with.
546+
/// * If `T` is not a zero-sized type and the capacity is nonzero, `ptr` must have
547+
/// been allocated using the global allocator, such as via the [`alloc::alloc`]
548+
/// function. If `T` is a zero-sized type or the capacity is zero, `ptr` need
549+
/// only be non-null and aligned.
550+
/// * `T` needs to have the same alignment as what `ptr` was allocated with,
551+
/// if the pointer is required to be allocated.
529552
/// (`T` having a less strict alignment is not sufficient, the alignment really
530553
/// needs to be equal to satisfy the [`dealloc`] requirement that memory must be
531554
/// allocated and deallocated with the same layout.)
532-
/// * The size of `T` times the `capacity` (ie. the allocated size in bytes) needs
533-
/// to be the same size as the pointer was allocated with. (Because similar to
534-
/// alignment, [`dealloc`] must be called with the same layout `size`.)
555+
/// * The size of `T` times the `capacity` (ie. the allocated size in bytes), if
556+
/// nonzero, needs to be the same size as the pointer was allocated with.
557+
/// (Because similar to alignment, [`dealloc`] must be called with the same
558+
/// layout `size`.)
535559
/// * `length` needs to be less than or equal to `capacity`.
536560
/// * The first `length` values must be properly initialized values of type `T`.
537-
/// * `capacity` needs to be the capacity that the pointer was allocated with.
561+
/// * `capacity` needs to be the capacity that the pointer was allocated with,
562+
/// if the pointer is required to be allocated.
538563
/// * The allocated size in bytes must be no larger than `isize::MAX`.
539564
/// See the safety documentation of [`pointer::offset`].
540565
///
@@ -770,12 +795,16 @@ impl<T> Vec<T> {
770795
/// order as the arguments to [`from_raw_parts`].
771796
///
772797
/// After calling this function, the caller is responsible for the
773-
/// memory previously managed by the `Vec`. The only way to do
774-
/// this is to convert the raw pointer, length, and capacity back
775-
/// into a `Vec` with the [`from_raw_parts`] function, allowing
776-
/// the destructor to perform the cleanup.
798+
/// memory previously managed by the `Vec`. Most often, one does
799+
/// this by converting the raw pointer, length, and capacity back
800+
/// into a `Vec` with the [`from_raw_parts`] function; more generally,
801+
/// if `T` is non-zero-sized and the capacity is nonzero, one may use
802+
/// any method that calls [`dealloc`] with a layout of
803+
/// `Layout::array::<T>(capacity)`; if `T` is zero-sized or the
804+
/// capacity is zero, nothing needs to be done.
777805
///
778806
/// [`from_raw_parts`]: Vec::from_raw_parts
807+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
779808
///
780809
/// # Examples
781810
///
@@ -1755,6 +1784,12 @@ impl<T, A: Allocator> Vec<T, A> {
17551784
/// may still invalidate this pointer.
17561785
/// See the second example below for how this guarantee can be used.
17571786
///
1787+
/// The method also guarantees that, as long as `T` is not zero-sized and the capacity is
1788+
/// nonzero, the pointer may be passed into [`dealloc`] with a layout of
1789+
/// `Layout::array::<T>(capacity)` in order to deallocate the backing memory. If this is done,
1790+
/// be careful not to run the destructor of the `Vec`, as dropping it will result in
1791+
/// double-frees. Wrapping the `Vec` in a [`ManuallyDrop`] is the typical way to achieve this.
1792+
///
17581793
/// # Examples
17591794
///
17601795
/// ```
@@ -1787,9 +1822,24 @@ impl<T, A: Allocator> Vec<T, A> {
17871822
/// }
17881823
/// ```
17891824
///
1825+
/// Deallocating a vector using [`Box`] (which uses [`dealloc`] internally):
1826+
///
1827+
/// ```
1828+
/// use std::mem::{ManuallyDrop, MaybeUninit};
1829+
///
1830+
/// let mut v = ManuallyDrop::new(vec![0, 1, 2]);
1831+
/// let ptr = v.as_mut_ptr();
1832+
/// let capacity = v.capacity();
1833+
/// let slice_ptr: *mut [MaybeUninit<i32>] =
1834+
/// std::ptr::slice_from_raw_parts_mut(ptr.cast(), capacity);
1835+
/// drop(unsafe { Box::from_raw(slice_ptr) });
1836+
/// ```
1837+
///
17901838
/// [`as_mut_ptr`]: Vec::as_mut_ptr
17911839
/// [`as_ptr`]: Vec::as_ptr
17921840
/// [`as_non_null`]: Vec::as_non_null
1841+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
1842+
/// [`ManuallyDrop`]: core::mem::ManuallyDrop
17931843
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
17941844
#[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")]
17951845
#[rustc_never_returns_null_ptr]

0 commit comments

Comments
 (0)