From 2e50e1a6ec8cbc8af486435df6d5de888c8ab26f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 26 Jul 2022 17:30:10 -0700 Subject: [PATCH] Explain how Vec::with_capacity is faithful There are concerns that the doc changes in rust-lang/rust@95dc353006c23a4493b3d08ba33680e51c2107c8 are breaking changes in the promised API. In this commit, I explain in more detail the exact promise that Vec::with_capacity is really making: Vec is trying to act unsurprising by relaying information faithfully to the allocator, it is not committing to internal details of Vec itself beyond that. As it happens, we don't get useful capacity information from allocators, but once upon a time Rust did capacity recalculation from available data, and we should reserve the right to do so again if it seems profitable and correct. This path avoids adding a duplicate `with_capacity_exact` to Vec's already-formidable API surface. --- library/alloc/src/vec/mod.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index fa9f2131c0c1d..cbb2a881cecb2 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -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 [len] == [capacity], -/// (as is the case for the [`vec!`] macro), then a `Vec` 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 [len] == [capacity], then a `Vec` 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 @@ -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]