Site

Vec<T> — Rust Growable Array

Tutorial S2.0  •  Rust / Learn / StdLib

S2.0 What This Teaches

Vec<T> is Rust's growable array - the most commonly used collection. This tutorial covers:

S2.1 Construction

let v1: Vec<i32> = Vec::new();          // empty; type must be annotated
let v2 = vec![1, 2, 3, 4, 5];           // macro shorthand
let v3: Vec<i32> = Vec::with_capacity(10); // reserves heap space; len stays 0
println!("v1 len={}, v3 capacity={}", v1.len(), v3.capacity());
with_capacity avoids repeated reallocations when you know the approximate final size. The vec![] macro is idiomatic for small, known-at-compile-time values.

S2.2 push and pop

push appends to the end; pop removes and returns the last element as Option<T>:
let mut v = vec![1, 2, 3];
v.push(4);
v.push(5);
println!("{v:?}");          // [1, 2, 3, 4, 5]

let last = v.pop();
println!("{last:?}");       // Some(5)
println!("{v:?}");          // [1, 2, 3, 4]
pop on an empty Vec returns None rather than panicking.

S2.3 insert and remove

insert(index, value) shifts elements right; remove(index) shifts left:
v.insert(1, 99);   // [1, 99, 2, 3, 4]
v.remove(1);       // [1, 2, 3, 4]
Both are O(n) because they move elements. For frequent insertions in the middle, consider VecDeque from std::collections.

S2.4 Indexing and Safe Access

v[i] panics if i is out of bounds. v.get(i) returns Option<&T> - safe for untrusted indices:
println!("{}", v[0]);           // 1
println!("{:?}", v.get(10));    // None
Use get whenever the index comes from user input or a calculation where you are not certain it is in range.

S2.5 Slicing

A slice &v[a..b] borrows a contiguous subsequence without copying:
let slice = &v[1..3];
println!("{slice:?}");   // [2, 3]
Slices have type &[T] - the same type accepted by most standard library functions that process sequences. Writing functions that take &[T] instead of &Vec<T> makes them usable with arrays and slices too.

S2.6 Sorting and Deduplication

sort uses an in-place stable sort. dedup removes consecutive duplicate elements - most effective immediately after sorting:
let mut nums = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
nums.sort();
println!("{nums:?}");   // [1, 1, 2, 3, 3, 4, 5, 5, 6, 9]
nums.dedup();
println!("{nums:?}");   // [1, 2, 3, 4, 5, 6, 9]
For floating-point values use sort_by(|a, b| a.partial_cmp(b).unwrap()) because f32/f64 do not implement Ord (NaN breaks total ordering).

S2.7 retain

retain removes every element for which the predicate returns false, in place:
let mut v = vec![1, 2, 3, 4, 5, 6];
v.retain(|n| n % 2 == 0);
println!("{v:?}");   // [2, 4, 6]

S2.8 extend, contains, position

let mut base = vec![1, 2, 3];
base.extend([4, 5, 6]);
println!("{base:?}");                                 // [1, 2, 3, 4, 5, 6]
println!("{}", base.contains(&3));                    // true
println!("{:?}", base.iter().position(|n| *n == 3));  // Some(2)
extend accepts any iterator or array, appending all its elements. position returns the index of the first match as Option<usize>.

S2.9 Example - All Together

// Vec - demonstrates construction, mutation, slicing, sorting, and iteration of Vec<T>.

fn main() {
    // --- construction ---
    println!("--- construction ---");
    let v1: Vec<i32> = Vec::new();
    let v2 = vec![1, 2, 3, 4, 5];
    let v3: Vec<i32> = Vec::with_capacity(10);
    println!("v1 len={}, v2={v2:?}, v3 capacity={}", v1.len(), v3.capacity());

    // --- push and pop ---
    println!("--- push / pop ---");
    let mut v = vec![1, 2, 3];
    v.push(4);
    v.push(5);
    println!("after push: {v:?}");
    let last = v.pop();
    println!("popped: {last:?}, remaining: {v:?}");

    // --- insert and remove ---
    println!("--- insert / remove ---");
    v.insert(1, 99);
    println!("after insert: {v:?}");
    v.remove(1);
    println!("after remove: {v:?}");

    // --- indexing and get ---
    println!("--- index / get ---");
    println!("v[0] = {}", v[0]);
    println!("v.get(10) = {:?}", v.get(10));

    // --- slicing ---
    println!("--- slice ---");
    let slice = &v[1..3];
    println!("slice [1..3]: {slice:?}");

    // --- iteration ---
    println!("--- iteration ---");
    for n in &v { print!("{n} "); }
    println!();

    // --- sort and dedup ---
    println!("--- sort / dedup ---");
    let mut nums = vec![3, 1, 4, 1, 5, 9, 2, 6, 5, 3];
    nums.sort();
    println!("sorted: {nums:?}");
    nums.dedup();
    println!("deduped: {nums:?}");

    // --- retain ---
    println!("--- retain ---");
    let mut evens = vec![1, 2, 3, 4, 5, 6];
    evens.retain(|n| n % 2 == 0);
    println!("evens: {evens:?}");

    // --- extend ---
    println!("--- extend ---");
    let mut base = vec![1, 2, 3];
    base.extend([4, 5, 6]);
    println!("extended: {base:?}");

    // --- contains and position ---
    println!("--- contains / position ---");
    println!("contains 3: {}", base.contains(&3));
    println!("position of 3: {:?}", base.iter().position(|n| *n == 3));

    // --- len and clear ---
    println!("--- len / clear ---");
    println!("len: {}", base.len());
    base.clear();
    println!("after clear, is_empty: {}", base.is_empty());
}
Expected output:
--- construction ---
v1 len=0, v2=[1, 2, 3, 4, 5], v3 capacity=10
--- push / pop ---
after push: [1, 2, 3, 4, 5]
popped: Some(5), remaining: [1, 2, 3, 4]
--- insert / remove ---
after insert: [1, 99, 2, 3, 4]
after remove: [1, 2, 3, 4]
--- index / get ---
v[0] = 1
v.get(10) = None
--- slice ---
slice [1..3]: [2, 3]
--- iteration ---
1 2 3 4
--- sort / dedup ---
sorted: [1, 1, 2, 3, 3, 4, 5, 5, 6, 9]
deduped: [1, 2, 3, 4, 5, 6, 9]
--- retain ---
evens: [2, 4, 6]
--- extend ---
extended: [1, 2, 3, 4, 5, 6]
--- contains / position ---
contains 3: true
position of 3: Some(2)
--- len / clear ---
len: 6
after clear, is_empty: true

S2.10 Exercise

Exercise
  • Build a Vec<i32> from the range 1..=20. Use retain to keep only values divisible by 3. Print the result.
  • Write a function median(v: &mut Vec<f64>) -> Option<f64> that sorts the vec and returns the middle element (or the average of the two middle elements for even length). Return None for an empty vec.
  • Write a function deduplicate<T: Eq + Ord>(v: &mut Vec<T>) that sorts and deduplicates in one step. Test it on vec!["b","a","b","c","a"].

S2.11 Common Mistakes

Indexing out of bounds

let v = vec![1, 2, 3];
println!("{}", v[5]);   // panics at runtime
Use v.get(5) to get None instead of a panic.

Mutating a Vec while iterating

let mut v = vec![1, 2, 3];
for i in 0..v.len() {
    v.push(i);   // error: cannot borrow `v` as mutable because it is borrowed
}
You cannot mutate a Vec while it is borrowed. Collect indices first, or restructure the loop.

Calling sort on f64

let mut floats = vec![3.0_f64, 1.0, 2.0];
floats.sort();   // error: f64 does not implement Ord
Use sort_by(|a, b| a.partial_cmp(b).unwrap()) for floating-point slices.

S2.12 Key Terms

TermMeaning
Vec<T>Heap-allocated growable array of T values
vec![]Macro that creates a Vec from a literal list
push / popAppend / remove-and-return the last element
insert / removeAdd or remove at an arbitrary index; O(n)
get(i)Returns Option<&T>; safe alternative to v[i]
&[T]Slice: a borrowed view of a contiguous sequence
retainIn-place filter: keeps elements matching a predicate
dedupRemoves consecutive duplicates; call after sort for full dedup
with_capacityPre-allocates heap space to avoid repeated reallocation