Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explain how Vec::with_capacity is faithful #99790

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions library/alloc/src/vec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,19 @@ mod spec_extend;
/// and it may prove desirable to use a non-constant growth factor. Whatever
/// strategy is used will of course guarantee *O*(1) amortized [`push`].
///
/// `vec![x; n]`, `vec![a, b, c, d]`, and
/// [`Vec::with_capacity(n)`][`Vec::with_capacity`], will all produce a `Vec`
/// with exactly the requested capacity. If <code>[len] == [capacity]</code>,
/// (as is the case for the [`vec!`] macro), then a `Vec<T>` can be converted to
/// and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements.
/// `vec![x; n]` and [`Vec::with_capacity(n)`] produce a `Vec` that allocates `n` capacity.
/// `vec![a, b, c, d, e]` produces a `Vec` which allocates once for all items (in this case 5).
/// An allocator may return an allocation with a size larger than the requested capacity.
/// In that case, [`capacity`] may return either the requested capacity or actual allocated size.
/// `Vec` has preferred either answer at different times and may change again.
/// However, `Vec::with_capacity(5)` will not deliberately "round up" to `Vec::with_capacity(8)`
/// for any non-zero-sized type, to respect programmer intent.
///
/// Excess capacity an allocator has given `Vec` is still discarded by [`shrink_to_fit`].
/// If <code>[len] == [capacity]</code>, then a `Vec<T>` can be converted
/// to and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements.
/// `Vec` exploits this fact as much as reasonable when implementing common conversions
/// such as [`into_boxed_slice`].
Comment on lines +361 to +373
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some suggestions for re-wording based on your stated intent, which I hope read a little clearer with a clearer distinction between "requested", "allocated", and "reported" capacities (none of which are guaranteed to equal any other).

Para 1: These are the ways of constructing with a specific capacity, they provide this guarantee on the allocation request.

Para 2: Allocator may return more space, and Vec is not guaranteed to report capacity as exactly the requested or the allocated capacity

Suggested change
/// `vec![x; n]` and [`Vec::with_capacity(n)`] produce a `Vec` that allocates `n` capacity.
/// `vec![a, b, c, d, e]` produces a `Vec` which allocates once for all items (in this case 5).
/// An allocator may return an allocation with a size larger than the requested capacity.
/// In that case, [`capacity`] may return either the requested capacity or actual allocated size.
/// `Vec` has preferred either answer at different times and may change again.
/// However, `Vec::with_capacity(5)` will not deliberately "round up" to `Vec::with_capacity(8)`
/// for any non-zero-sized type, to respect programmer intent.
///
/// Excess capacity an allocator has given `Vec` is still discarded by [`shrink_to_fit`].
/// If <code>[len] == [capacity]</code>, then a `Vec<T>` can be converted
/// to and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements.
/// `Vec` exploits this fact as much as reasonable when implementing common conversions
/// such as [`into_boxed_slice`].
/// `vec![x; n]` and [`Vec::with_capacity(n)`] produce a `Vec` that allocates `n` capacity;
/// that is, they request a capacity for `n` elements from the allocator. Similarly,
/// `vec![a, b, c, d, e]` requests an allocation to cover the number of given elements (in this case 5).
/// `Vec` is guaranteed request exactly these capacities and not "round up" the allocation request
/// to speculatively avoid potential future allocations in these cases, to respect programmer
/// intent.
///
/// Any allocator may return an allocation with a size larger than the requested capacity, so
/// the allocated capacity may exceed the requested capacity. The reported capacity, as returned
/// by [`capacity`], is guaranteed to be at least the requested capacity and not more than the
/// allocated capacity, but is not guaranteed to be either. So if the programmer requests a
/// capacity of `n` the the allocator will be asked for `n` but may allocate space for `m >= n`, and
/// [`capacity`] may therefore also return `c >= n` (but `c <= m` is guaranteed).
///
/// Excess capacity an allocator has given `Vec` is still discarded by [`shrink_to_fit`].
/// If <code>[len] == [capacity]</code>, then a `Vec<T>` can be converted
/// to and from a [`Box<[T]>`][owned slice] without reallocating or moving the elements.
/// `Vec` exploits this fact as much as reasonable when implementing common conversions
/// such as [`into_boxed_slice`].

///
/// `Vec` will not specifically overwrite any data that is removed from it,
/// but also won't specifically preserve it. Its uninitialized memory is
Expand Down Expand Up @@ -392,8 +400,10 @@ mod spec_extend;
/// [`push`]: Vec::push
/// [`insert`]: Vec::insert
/// [`reserve`]: Vec::reserve
/// [`Vec::with_capacity(n)`]: Vec::with_capacity
/// [`MaybeUninit`]: core::mem::MaybeUninit
/// [owned slice]: Box
/// [`into_boxed_slice`]: Vec::into_boxed_slice
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Vec")]
#[rustc_insignificant_dtor]
Expand Down