Vectors

Rust · Practical · 10 min

Vec is the default collection in Rust; most data you process will pass through one.

`Vec<T>` is a growable, heap-allocated array of T. Create one with Vec::new(), Vec::with_capacity(n) when you know roughly how many elements are coming, or the vec![1, 2, 3] macro.

Adding and removing: push, pop (returns Option<T>), insert(i, x), remove(i) (shifts later elements, O(n)), swap_remove(i) (O(1), does not preserve order), extend, truncate, clear, retain(|x| ...) to filter in place, and dedup after a sort.

Reading: v[i] panics if out of bounds, while v.get(i) returns an Option<&T>. Use get when the index comes from outside your control.

Because a Vec<T> derefs to a slice [T], every slice method is available: len, is_empty, contains, iter, sort, sort_by_key, binary_search, windows, chunks, first, last, split_at, and more. So you can pass &v wherever a &[T] is expected.

Iteration comes in three flavours:

  • for x in &v borrows each element (&T),
  • for x in &mut v gives &mut T for in-place edits,
  • for x in v consumes the vector and yields owned T.

Under the hood a Vec is a pointer, a length and a capacity. When len reaches capacity, pushing reallocates (roughly doubling) and moves all elements. That is why the borrow checker forbids holding a reference into a vector across a push. shrink_to_fit releases spare capacity.

To store values of different types in one Vec, wrap them in an enum or use trait objects (covered later).

Example

fn main() {
    let mut scores: Vec<u32> = Vec::with_capacity(4);
    scores.extend([70, 95, 82]);
    scores.push(95);

    println!("{:?} {:?}", scores.get(10), scores.first()); // None Some(70)

    for s in &mut scores {
        *s += 5; // in-place edit through &mut
    }
    scores.sort_unstable();
    scores.dedup();
    scores.retain(|&s| s >= 80);
    println!("{scores:?} len={} cap>={}", scores.len(), scores.capacity()); // [87, 100] len=2 cap>=4

    let pairs: Vec<&[u32]> = [1, 2, 3, 4].windows(2).collect::<Vec<_>>();
    println!("{pairs:?}"); // [[1, 2], [2, 3], [3, 4]]

    let total: u32 = scores.into_iter().sum(); // consumes the vector
    println!("{total}"); // 187
}

Remember

  • Use vec![] or Vec::with_capacity; get returns Option, [] panics
  • A Vec derefs to a slice, so all slice methods apply
  • &v, &mut v and v in a for loop borrow, mutably borrow, or consume

Watch out for

  • Calling remove(0) repeatedly on a large Vec (O(n) each); use VecDeque for queue behaviour.

Check yourself

  1. Why can't you keep `let first = &v[0];` alive across `v.push(9)`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: doc.rust-lang.org/book/ch08-01-vectors.html