From 8223789fcd6259d28c2dbcba6001fa3054a71777 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 19 Aug 2015 10:13:39 -0700 Subject: [PATCH 1/3] 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 ec3c36d0c8137..f21d6835e1886 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 06d1c0cb71c2e8b1dfc4c3b824177ae58197b586 Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 19 Aug 2015 10:17:35 -0700 Subject: [PATCH 2/3] remove references to deprecate collections from docs --- src/libstd/collections/mod.rs | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 4367dda84663e..52a96ebd65c13 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -25,9 +25,9 @@ //! //! Rust's collections can be grouped into four major categories: //! -//! * Sequences: `Vec`, `VecDeque`, `LinkedList`, `BitVec` -//! * Maps: `HashMap`, `BTreeMap`, `VecMap` -//! * Sets: `HashSet`, `BTreeSet`, `BitSet` +//! * Sequences: `Vec`, `VecDeque`, `LinkedList` +//! * Maps: `HashMap`, `BTreeMap` +//! * Sets: `HashSet`, `BTreeSet` //! * Misc: `BinaryHeap` //! //! # When Should You Use Which Collection? @@ -70,22 +70,11 @@ //! * You want to be able to get all of the entries in order on-demand. //! * You want a sorted map. //! -//! ### Use a `VecMap` when: -//! * You want a `HashMap` but with known to be small `usize` keys. -//! * You want a `BTreeMap`, but with known to be small `usize` keys. -//! //! ### Use the `Set` variant of any of these `Map`s when: //! * You just want to remember which keys you've seen. //! * There is no meaningful value to associate with your keys. //! * You just want a set. //! -//! ### Use a `BitVec` when: -//! * You want to store an unbounded number of booleans in a small space. -//! * You want a bit vector. -//! -//! ### Use a `BitSet` when: -//! * You want a `BitVec`, but want `Set` properties -//! //! ### Use a `BinaryHeap` when: //! //! * You want to store a bunch of elements, but only ever want to process the @@ -123,11 +112,9 @@ //! | Vec | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | //! | VecDeque | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | //! | LinkedList | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | -//! | BitVec | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | //! //! Note that where ties occur, Vec is generally going to be faster than VecDeque, and VecDeque -//! is generally going to be faster than LinkedList. BitVec is not a general purpose collection, and -//! therefore cannot reasonably be compared. +//! is generally going to be faster than LinkedList. //! //! ## Maps //! @@ -139,15 +126,6 @@ //! |----------|-----------|----------|----------|-------------| //! | HashMap | O(1)~ | O(1)~* | O(1)~ | N/A | //! | BTreeMap | O(log n) | O(log n) | O(log n) | O(log n) | -//! | VecMap | O(1) | O(1)? | O(1) | O(n) | -//! -//! Note that VecMap is *incredibly* inefficient in terms of space. The O(1) -//! insertion time assumes space for the element is already allocated. -//! Otherwise, a large key may require a massive reallocation, with no direct -//! relation to the number of elements in the collection. VecMap should only be -//! seriously considered for small keys. -//! -//! Note also that BTreeMap's precise performance depends on the value of B. //! //! # Correct and Efficient Usage of Collections //! From eb43f495027303a99e583647ec73a63d488c765d Mon Sep 17 00:00:00 2001 From: Alexis Beingessner Date: Wed, 19 Aug 2015 12:36:14 -0700 Subject: [PATCH 3/3] 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 f21d6835e1886..027ea04abbaab 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