Skip to content

Commit e561d06

Browse files
committed
document FromIterator for Vec allocation behaviors
1 parent 5bd5d21 commit e561d06

File tree

1 file changed

+45
-0
lines changed

1 file changed

+45
-0
lines changed

library/alloc/src/vec/mod.rs

+45
Original file line numberDiff line numberDiff line change
@@ -2792,6 +2792,51 @@ impl<T, I: SliceIndex<[T]>, A: Allocator> IndexMut<I> for Vec<T, A> {
27922792
}
27932793
}
27942794

2795+
/// Collects an iterator into a Vec, commonly via [`Iterator::collect()`]
2796+
///
2797+
/// # Allocation behavior
2798+
///
2799+
/// In general `Vec` does not guarantee any particular grow/allocation stategy.
2800+
/// That also applies to this trait impl.
2801+
///
2802+
/// **Note:** This section covers implementation details and is therefore exempt from
2803+
/// stability guarantees.
2804+
///
2805+
/// Vec may use any or none of the following strategies,
2806+
/// depending on the supplied iterator:
2807+
///
2808+
/// * preallocate based on [`Iterator::size_hint()`]
2809+
/// * and panic if the number of items is not outside the provided lower/upper bounds
2810+
/// * use an amortized growth strategy similar to `pushing` one item at a time
2811+
/// * perform the iteration in-place on the original allocation backing the iterator
2812+
///
2813+
/// The last case warrants some attention. It is an optimization that in many cases reduces peak memory
2814+
/// consumption and improves cache locality. But when a large number of big, short-lived
2815+
/// allocations are created, only a small fraction of their items gets collected, no further use
2816+
/// is made of the spare capacity and the resulting `Vec` is moved into a longer-lived structure
2817+
/// this can lead to the large allocations having their lifetimes unnecessarily extended which
2818+
/// can result in increased memory footprint.
2819+
///
2820+
/// In cases where this is an issue the excess capacity can be discard with [`Vec::shrink_to()`],
2821+
/// [`Vec::shrink_to_fit()`] or by collecting into [`Box<[T]>`][owned slice] instead which additionally reduces
2822+
/// the size of the longlived struct.
2823+
///
2824+
/// [owned slice]: Box
2825+
///
2826+
/// ```rust
2827+
/// # use std::sync::Mutex;
2828+
/// static LONG_LIVED: Mutex<Vec<Vec<u16>>> = Mutex::new(Vec::new());
2829+
///
2830+
/// // many short-lived allocations
2831+
/// for i in 0..100 {
2832+
/// let big_temporary: Vec<u16> = (0..1024).collect();
2833+
/// // discard most items
2834+
/// let mut result: Vec<_> = big_temporary.into_iter().filter(|i| i % 100 == 0).collect();
2835+
/// // without this a lot of unused capacity might be moved into the global
2836+
/// result.shrink_to_fit();
2837+
/// LONG_LIVED.lock().unwrap().push(result);
2838+
/// }
2839+
/// ```
27952840
#[cfg(not(no_global_oom_handling))]
27962841
#[stable(feature = "rust1", since = "1.0.0")]
27972842
impl<T> FromIterator<T> for Vec<T> {

0 commit comments

Comments
 (0)