Skip to content

Commit 79b02dc

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 4c83108 + 45e2449 commit 79b02dc

File tree

1 file changed

+47
-4
lines changed

1 file changed

+47
-4
lines changed

library/alloc/src/vec/mod.rs

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

@@ -770,12 +789,15 @@ impl<T> Vec<T> {
770789
/// order as the arguments to [`from_raw_parts`].
771790
///
772791
/// 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.
792+
/// memory previously managed by the `Vec`. Most often, one does
793+
/// this by converting the raw pointer, length, and capacity back
794+
/// into a `Vec` with the [`from_raw_parts`] function; more generally,
795+
/// if `T` is non-zero-sized one may use any method that calls
796+
/// [`dealloc`] with a layout of `Layout::array::<T>(capacity)`,
797+
/// and if `T` is zero-sized nothing needs to be done.
777798
///
778799
/// [`from_raw_parts`]: Vec::from_raw_parts
800+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
779801
///
780802
/// # Examples
781803
///
@@ -1755,6 +1777,12 @@ impl<T, A: Allocator> Vec<T, A> {
17551777
/// may still invalidate this pointer.
17561778
/// See the second example below for how this guarantee can be used.
17571779
///
1780+
/// The method also guarantees that, as long as `T` is not zero-sized, the pointer may be
1781+
/// passed into [`dealloc`] with a layout of `Layout::array::<T>(capacity)` in order to
1782+
/// deallocate the backing memory. If this is done, be careful not to run the destructor
1783+
/// of the `Vec`, as dropping it will result in double-frees. Wrapping the `Vec` in a
1784+
/// [`ManuallyDrop`] is the typical way to achieve this.
1785+
///
17581786
/// # Examples
17591787
///
17601788
/// ```
@@ -1787,9 +1815,24 @@ impl<T, A: Allocator> Vec<T, A> {
17871815
/// }
17881816
/// ```
17891817
///
1818+
/// Deallocating a vector using [`Box`] (which uses [`dealloc`] internally):
1819+
///
1820+
/// ```
1821+
/// use std::mem::{ManuallyDrop, MaybeUninit};
1822+
///
1823+
/// let mut v = ManuallyDrop::new(vec![0, 1, 2]);
1824+
/// let ptr = v.as_mut_ptr();
1825+
/// let capacity = v.capacity();
1826+
/// let slice_ptr: *mut [MaybeUninit<i32>] =
1827+
/// std::ptr::slice_from_raw_parts_mut(ptr.cast(), capacity);
1828+
/// drop(unsafe { Box::from_raw(slice_ptr) });
1829+
/// ```
1830+
///
17901831
/// [`as_mut_ptr`]: Vec::as_mut_ptr
17911832
/// [`as_ptr`]: Vec::as_ptr
17921833
/// [`as_non_null`]: Vec::as_non_null
1834+
/// [`dealloc`]: crate::alloc::GlobalAlloc::dealloc
1835+
/// [`ManuallyDrop`]: core::mem::ManuallyDrop
17931836
#[stable(feature = "vec_as_ptr", since = "1.37.0")]
17941837
#[rustc_const_stable(feature = "const_vec_string_slice", since = "1.87.0")]
17951838
#[rustc_never_returns_null_ptr]

0 commit comments

Comments
 (0)