管理容量
影响 Vec<T>
的容量的方法有很多种, 接下来我们分别列举主要的几种形式.
Vec 分配的堆内存大小等于 size_of::<T>() * vec.capacity()
.
new() 函数
这个函数就比较简单了, 它并不会分配堆内存, 容量等于 0
.
fn main() { let numbers = Vec::<i32>::new(); assert_eq!(numbers.len(), 0); assert_eq!(numbers.capacity(), 0); }
可以看看标准库中 Vec::new()
的代码实现:
#![allow(unused)] fn main() { impl<T> Vec<T> { #[inline] #[must_use] pub const fn new() -> Self { Vec { buf: RawVec::NEW, len: 0 } } } impl<T> RawVec<T> { /// HACK(Centril): This exists because stable `const fn` can only call stable `const fn`, so /// they cannot call `Self::new()`. /// /// If you change `RawVec<T>::new` or dependencies, please take care to not introduce anything /// that would truly const-call something unstable. pub const NEW: Self = Self::new(); /// Creates the biggest possible `RawVec` (on the system heap) /// without allocating. If `T` has positive size, then this makes a /// `RawVec` with capacity `0`. If `T` is zero-sized, then it makes a /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] pub const fn new() -> Self { Self::new_in() } /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. pub const fn new_in() -> Self { // `cap: 0` means "unallocated". zero-sized types are ignored. Self { ptr: Unique::dangling(), cap: Cap::ZERO } } } }
with_capacity(cap) 函数
该函数在创建 vec 的同时, 还给它分配足够多的堆内存, 以便存放 cap
个元素.
vec![] 宏, 以及从迭代器创建
从这两种方法创建 vec 对象时, 都要先获取元素的个数 len
, 然后设置新创建的 vec 对象的容量恰好等于 len
,
看下面的代码片段:
#![allow(unused)] fn main() { let v1 = vec![1, 2, 3]; let v2: Vec<i32> = [1, 2, 3].into_iter().collect(); assert_eq!(v1, v2); assert_eq!(v1.capacity(), 3); assert_eq!(v2.capacity(), 3); }
push() 函数, 向 vec 中加入新元素
以下的代码示例展示了 vec 的扩容策略, 那就是 2 倍扩容
, 不管当前的 vec 对象已经占用了多大的内存,
在需要扩容时, 一直都是 2倍扩容
.
use std::mem::size_of; fn main() { let mut v1 = Vec::<i32>::new(); println!("len of v1: {}, cap: {}", v1.len(), v1.capacity()); v1.push(1); println!("len of v1: {}, cap: {}", v1.len(), v1.capacity()); v1.push(2); v1.push(3); v1.push(4); v1.push(5); println!("len of v1: {}, cap: {}", v1.len(), v1.capacity()); let mut v2: Vec<i64> = Vec::new(); println!( "len of v2: {}, cap: {}, size: {}", v2.len(), v2.capacity(), v2.capacity() * size_of::<i64>() ); let mut old_cap = v2.capacity(); for i in 0..10_000_000 { v2.push(i); if v2.capacity() != old_cap { old_cap = v2.capacity(); println!( "len of v2: {}, cap: {}, size: {}", v2.len(), v2.capacity(), v2.capacity() * size_of::<i64>() ); } } }
pop() 函数, 从 vec 中移除元素, 会不会自动释放内存?
先看一下测试代码:
fn main() { let mut v1: Vec<i32> = Vec::new(); for i in 0..1_000_000 { v1.push(i); } println!("capacity: {}, len: {}", v1.capacity(), v1.len()); while v1.pop().is_some() { // Ignore } println!("capacity: {}, len: {}", v1.capacity(), v1.len()); // 手动释放内存 v1.shrink_to_fit(); println!("shrink_to_fit(), capacity: {}, len: {}", v1.capacity(), v1.len()); }
可以发现, 它并不会自动释放多余的内存, 需要手动调用 resize()
, shrink_to_fit()
等函数.
reverse(additional) 函数
这个函数要求数组至少预留 additional
个元数的空间.
fn main() { let mut v1 = vec![1, 2, 3]; println!("len: {}, cap: {}", v1.len(), v1.capacity()); v1.reserve(12); println!("len: {}, cap: {}", v1.len(), v1.capacity()); v1.reserve(13); println!("len: {}, cap: {}", v1.len(), v1.capacity()); }
可以发现, 上面的例子中, 数组多分配了一些空间. 我们看一下标准库中的代码:
#![allow(unused)] fn main() { fn grow_amortized(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { ... // Nothing we can really do about these checks, sadly. let required_cap = len.checked_add(additional).ok_or(CapacityOverflow)?; // This guarantees exponential growth. The doubling cannot overflow // because `cap <= isize::MAX` and the type of `cap` is `usize`. let cap = cmp::max(self.cap.0 * 2, required_cap); let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap); let new_layout = Layout::array::<T>(cap); ... } }
如果要留出的空间比当前容量的 2
倍少的话, 会直接使用 2 倍扩容
的策略.
shrink_to_fit() 函数
这个函数比较容易理解, 调整数组的容量, 移除多余的未使用的内存, 这样 len() == capacity()
.
resize() 函数
调整数组中的元素个数, 如果新的个数比当前的少, 就移除一部分;
如果比当前的个数多, 就添加一部分, 使用指定的值, 同时数组的容量调整, 依然是按照 2 倍扩容
的策略.
fn main() { let mut v1: Vec<i32> = Vec::new(); v1.resize(42, 1); println!("len: {}, cap: {}", v1.len(), v1.capacity()); v1.resize(52, 2); println!("len: {}, cap: {}", v1.len(), v1.capacity()); v1.resize(32, 2); println!("len: {}, cap: {}", v1.len(), v1.capacity()); }