They are currently represented them as `*{ length, capacity, data[] }`. It means we have to handle trickier `uint` overflow cases, which increases cost size a fair bit. It also means even an empty vector always requires a heap allocation. C++ represents a vector as `{ length, capacity, *data }`, so a fresh empty vector simply as a `null` data pointer. Unlike the `OptVec` optimization, it does not require an extra branch in every method. Calling `realloc` on a null pointer is well-defined, so you can ignore the special case. This has the drawback of making a move involve a 3-word copy, not 1-word. However, passing around unique vectors by-move rather than slices is not common enough to be a very relevant performance issue. Benchmarks on x86_64 Linux (linux 3.11.5, glibc 2.18): | Comparison | ~[T] | Proposal | | --- | --- | --- | | Empty | 29 ns/iter (+/- 1) | 2 ns/iter (+/- 0) | | Push 10k, starting at 16 capacity | 45528 ns/iter (+/- 2584) | 7611 ns/iter (+/- 407) | | Push 10k, starting at 10k capacity | 43783 ns/iter (+/- 1429) | 6876 ns/iter (+/- 320) | | Size | `size_of::<uint>` | `3 * size_of::<uint>` | | Empty heap allocation size | minimum `2 * size_of::<uint>` | n/a | | 4 element heap allocation size | `2 * size_of::<uint> + 4 * size_of::<T>` | `4 * size_of::<T>` | Note that since jemalloc is very clever, at some sizes preallocating has next to no advantage for the new proposed vector too. Implementation: ``` rust #[allow(ctypes)]; #[cfg(test)] extern mod extra; use std::mem::size_of; use std::cast::transmute; use std::unstable::raw::Slice; use std::unstable::intrinsics::move_val_init; use std::ptr::read_ptr; // redefine these to use `*mut` ... extern { fn malloc(size: uint) -> *mut u8; fn realloc(ptr: *mut u8, size: uint) -> *mut u8; fn free(ptr: *mut u8); fn abort() -> !; } #[fixed_stack_segment] unsafe fn malloc_raw(size: uint) -> *mut u8 { let ptr = malloc(size); if ptr.is_null() { abort() } ptr } #[fixed_stack_segment] unsafe fn realloc_raw(ptr: *mut u8, size: uint) -> *mut u8 { let ptr = realloc(ptr, size); if ptr.is_null() { abort() } ptr } #[fixed_stack_segment] unsafe fn free_raw(ptr: *mut u8) { free(ptr) } struct Vec<T> { priv len: uint, priv cap: uint, priv ptr: *mut T } impl<T> Vec<T> { #[inline] fn new() -> Vec<T> { Vec { len: 0, cap: 0, ptr: 0 as *mut T } } #[inline] fn with_capacity(capacity: uint) -> Vec<T> { let size = capacity.checked_mul(&size_of::<T>()).unwrap(); unsafe { Vec { len: 0, cap: capacity, ptr: malloc_raw(size) as *mut T } } } #[inline] fn shrink_to_fit(&mut self) { unsafe { self.cap = self.len; if self.len == 0 { free_raw(self.ptr as *mut u8); self.ptr = 0 as *mut T; } else { self.ptr = realloc_raw(self.ptr as *mut u8, self.cap * size_of::<T>()) as *mut T; } } } #[inline] fn push(&mut self, value: T) { unsafe { if self.len == self.cap { if self.cap == 0 { self.cap += 2 } let old_size = self.cap * size_of::<T>(); self.cap = self.cap * 2; let size = old_size.checked_mul(&2).unwrap(); self.ptr = realloc_raw(self.ptr as *mut u8, size) as *mut T; } let end = self.ptr.offset(self.len as int); move_val_init(&mut *end, value); self.len += 1; } } #[inline] fn as_slice<'r>(&'r self) -> &'r [T] { let slice = Slice { data: self.ptr as *T, len: self.len }; unsafe { transmute(slice) } } #[inline] fn as_mut_slice<'r>(&'r mut self) -> &'r mut [T] { let slice = Slice { data: self.ptr as *T, len: self.len }; unsafe { transmute(slice) } } } #[unsafe_destructor] impl<T> Drop for Vec<T> { fn drop(&mut self) { unsafe { for x in self.as_mut_slice().mut_iter() { read_ptr(x); } free_raw(self.ptr as *mut u8) } } } #[cfg(test)] mod bench { use std; use super::Vec; use extra::test::BenchHarness; #[bench] fn empty(bh: &mut BenchHarness) { do bh.iter() { let _xs: Vec<int> = Vec::new(); } } #[bench] fn empty_std(bh: &mut BenchHarness) { do bh.iter() { let _xs: ~[int] = ~[]; } } static size: uint = 10000; #[bench] fn push(bh: &mut BenchHarness) { do bh.iter() { let mut xs = Vec::with_capacity(16); for i in range(0, size) { xs.push(i); } } } #[bench] fn push_std(bh: &mut BenchHarness) { do bh.iter() { let mut xs = std::vec::with_capacity(16); for i in range(0, size) { xs.push(i); } } } #[bench] fn push_preallocated(bh: &mut BenchHarness) { do bh.iter() { let mut xs = Vec::with_capacity(size); for i in range(0, size) { xs.push(i); } } } #[bench] fn push_preallocated_std(bh: &mut BenchHarness) { do bh.iter() { let mut xs = std::vec::with_capacity(size); for i in range(0, size) { xs.push(i); } } } } #[test] fn push() { let mut xs = Vec::new(); for x in std::iter::range_step(0, 100, 5) { xs.push(x) } let expected = std::iter::range_step(0, 100, 5).to_owned_vec(); assert_eq!(xs.as_slice(), expected.as_slice()); } ```