From 9ef2651dff3bdee3fd91d474a15dea9a4b5ece08 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 19 Aug 2015 10:13:39 -0700 Subject: [PATCH 1/2] flesh out what Vec actually guarantees --- src/libcollections/vec.rs | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index f2ede5c44d312..ee44fb0541ec1 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -148,6 +148,81 @@ use super::range::RangeArgument; /// if the vector's length is increased to 11, it will have to reallocate, which /// can be slow. For this reason, it is recommended to use `Vec::with_capacity` /// whenever possible to specify how big the vector is expected to get. +/// +/// # Guarantees +/// +/// Due to its incredibly fundamental nature, Vec makes a lot of guarantees +/// about its design. This ensures that it's as low-overhead as possible in +/// the general case, and can be correctly manipulated in primitive ways +/// by unsafe code. Note that these guarantees refer to an unqualified `Vec`. +/// If additional type parameters are added (e.g. to support custom allocators), +/// overriding their defaults may change the behavior. +/// +/// Most fundamentally, Vec is and always will be a (pointer, capacity, length) +/// triplet. No more, no less. The order of these fields is completely +/// unspecified, and you should use the appropriate methods to modify these. +/// The pointer will never be null, so this type is null-pointer-optimized. +/// +/// However, the pointer may not actually point to allocated memory. In particular, +/// if you construct a Vec with capacity 0 via `Vec::new()`, `vec![]`, +/// `Vec::with_capacity(0)`, or by calling `shrink_to_fit()` on an empty Vec, it +/// will not allocate memory. Similarly, if you store zero-sized types inside +/// a Vec, it will not allocate space for them. *Note that in this case the +/// Vec may not report a `capacity()` of 0*. Vec will allocate if and only +/// if `mem::size_of::() * capacity() > 0`. In general, Vec's allocation +/// details are subtle enough that it is strongly recommended that you only +/// free memory allocated by a Vec by creating a new Vec and dropping it. +/// +/// If a Vec *has* allocated memory, then the memory it points to is on the heap +/// (as defined by the allocator Rust is configured to use by default), and its +/// pointer points to `len()` initialized elements in order (what you would see +/// if you coerced it to a slice), followed by `capacity() - len()` logically +/// uninitialized elements. +/// +/// Vec will never perform a "small optimization" where elements are actually +/// stored on the stack for two reasons: +/// +/// * It would make it more difficult for unsafe code to correctly manipulate +/// a Vec. The contents of a Vec wouldn't have a stable address if it were +/// only moved, and it would be more difficult to determine if a Vec had +/// actually allocated memory. +/// +/// * It would penalize the general case, incurring an additional branch +/// on every access. +/// +/// Vec will never automatically shrink itself, even if completely empty. This +/// ensures no unnecessary allocations or deallocations occur. Emptying a Vec +/// and then filling it back up to the same `len()` should incur no calls to +/// the allocator. If you wish to free up unused memory, use `shrink_to_fit`. +/// +/// `push` and `insert` will never (re)allocate if the reported capacity is +/// sufficient. `push` and `insert` *will* (re)allocate if `len() == capacity()`. +/// That is, the reported capacity is completely accurate, and can be relied on. +/// It can even be used to manually free the memory allocated by a Vec if +/// desired. Bulk insertion methods *may* reallocate unnecessarily. +/// +/// Vec does not guarantee any particular growth strategy when reallocating +/// when full, nor when `reserve` is called. The current strategy is basic +/// 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)`, 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]>` without reallocating or moving the elements. +/// +/// Vec will not specifically overwrite any data that is removed from it, +/// but also won't specifically preserve it. Its uninitialized memory is +/// scratch space that it may use however it wants. It will generally just do +/// whatever is most efficient or otherwise easy to implement. Do not rely on +/// removed data to be erased for security purposes. Even if you drop a Vec, its +/// buffer may simply be reused by another Vec. Even if you zero a Vec's memory +/// first, that may not actually happen because the optimizer does not consider +/// this a side-effect that must be preserved. +/// +/// Vec does not currently guarantee the order in which elements are dropped +/// (the order has changed in the past, and may change again). +/// #[unsafe_no_drop_flag] #[stable(feature = "rust1", since = "1.0.0")] pub struct Vec { From c7527a199953d4f902241279fb457c86c80f5927 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 19 Aug 2015 12:36:14 -0700 Subject: [PATCH 2/2] fixup wording --- src/libcollections/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index ee44fb0541ec1..bcde523307c83 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -199,7 +199,7 @@ use super::range::RangeArgument; /// sufficient. `push` and `insert` *will* (re)allocate if `len() == capacity()`. /// That is, the reported capacity is completely accurate, and can be relied on. /// It can even be used to manually free the memory allocated by a Vec if -/// desired. Bulk insertion methods *may* reallocate unnecessarily. +/// desired. Bulk insertion methods *may* reallocate, even when not necessary. /// /// Vec does not guarantee any particular growth strategy when reallocating /// when full, nor when `reserve` is called. The current strategy is basic