ZetCode

Rust vectors

last modified October 25, 2026

In this article we show how to work with vectors in Rust.

A vector, Vec<T>, is a growable array of elements of the same type. It is the most commonly used collection in Rust. The elements are stored contiguously on the heap, so a vector provides fast indexing and efficient iteration. Unlike an array, whose size is part of its type, a vector can grow and shrink while the program runs.

A vector consists of a pointer to the heap buffer, its length (the number of elements), and its capacity (the number of elements the buffer can hold). When the length reaches the capacity, the vector allocates a larger buffer and moves the elements there. The vector owns its elements; when it goes out of scope, the elements are dropped and the memory is freed.

The examples were tested with Rust 1.97 and the 2024 edition. They are run as Cargo projects with cargo run -q.

Creating vectors

A vector is created with the vec! macro, with the Vec::new and Vec::with_capacity functions, or by collecting an iterator.

main.rs
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let zeros = vec![0; 5];

    let mut words = Vec::new();
    for w in ["Hello", "Rust"] {
        words.push(w.to_uppercase());
    }

    let mut data: Vec<i32> = Vec::with_capacity(100);
    for i in 0..10 {
        data.push(i * 2);
    }

    let squares: Vec<u32> = (1..=5).map(|x| x * x).collect();
    let chars = Vec::from(['a', 'b', 'c']);
    let empty: Vec<f64> = Vec::new();

    println!("numbers: {numbers:?}");
    println!("zeros: {zeros:?}");
    println!("words: {words:?}");
    println!("data (capacity {}): {data:?}", data.capacity());
    println!("squares: {squares:?}");
    println!("chars: {chars:?}");
    println!("empty: {empty:?}, len {}", empty.len());
}

The example creates vectors in several ways.

let numbers = vec![1, 2, 3, 4, 5];
let zeros = vec![0; 5];

The vec! macro creates a vector from a list of values. The vec![value; n] form creates a vector with n copies of the value.

let mut words = Vec::new();
for w in ["Hello", "Rust"] {
    words.push(w.to_uppercase());
}

The Vec::new function creates an empty vector, which does not allocate memory until the first element is added. The element type is inferred from the push calls.

let mut data: Vec<i32> = Vec::with_capacity(100);

The with_capacity function allocates space for a hundred elements in advance. The vector is still empty; it can grow to a hundred elements without reallocating.

let squares: Vec<u32> = (1..=5).map(|x| x * x).collect();
let chars = Vec::from(['a', 'b', 'c']);
let empty: Vec<f64> = Vec::new();

The collect method builds a vector from an iterator, and Vec::from creates it from an array. An empty vector needs a type annotation if the type cannot be inferred from later use.

$ cargo run -q
numbers: [1, 2, 3, 4, 5]
zeros: [0, 0, 0, 0, 0]
words: ["HELLO", "RUST"]
data (capacity 100): [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
squares: [1, 4, 9, 16, 25]
chars: ['a', 'b', 'c']
empty: [], len 0

Accessing elements

Elements are accessed by indexing with [] or with the get method. Indexes start from zero.

main.rs
fn main() {
    let mut fruits = vec!["Apple", "Banana", "Cherry"];
    fruits.push("Date");

    let first = fruits[0];
    let last = fruits[fruits.len() - 1];
    println!("first: {first}, last: {last}");

    match fruits.get(2) {
        Some(fruit) => println!("index 2: {fruit}"),
        None => println!("index out of bounds"),
    }

    if let Some(fruit) = fruits.get(10) {
        println!("found: {fruit}");
    } else {
        println!("index 10 does not exist");
    }

    println!("{:?} {:?}", fruits.first(), fruits.last());
    println!("{:?}", fruits.get(1..3));
}

We get elements by index and with methods that return an Option.

let first = fruits[0];
let last = fruits[fruits.len() - 1];

Indexing returns the element directly. It panics if the index is out of bounds.

match fruits.get(2) {
    Some(fruit) => println!("index 2: {fruit}"),
    None => println!("index out of bounds"),
}

The get method returns Option<&T>: a reference to the element, or None if the index is out of bounds. The compiler forces us to handle the missing element.

println!("{:?} {:?}", fruits.first(), fruits.last());
println!("{:?}", fruits.get(1..3));

The first and last methods return None for an empty vector. Given a range, get returns a slice.

$ cargo run -q
first: Apple, last: Date
index 2: Cherry
index 10 does not exist
Some("Apple") Some("Date")
Some(["Banana", "Cherry"])

Index out of bounds

Rust checks every index at run time. An invalid index causes a panic instead of reading memory outside of the vector.

main.rs
fn main() {
    let fruits: Vec<&str> = "Apple Banana Cherry Date".split(' ').collect();
    let idx = fruits.len();

    println!("{}", fruits[idx]);
}

The vector has four elements, so the valid indexes are 0 to 3.

println!("{}", fruits[idx]);

The index 4 is equal to the length. The panic message shows the length and the index. Use indexing when an invalid index is a bug, and get when the index comes from user input or other uncertain data.

$ cargo run -q

thread 'main' (71506) panicked at src/main.rs:5:26:
index out of bounds: the len is 4 but the index is 4
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Modifying elements

Elements of a mutable vector can be changed through indexing, mutable references, and methods that change the whole vector.

main.rs
fn main() {
    let mut vals = vec![1, 2, 3, 4, 5];

    vals[0] = 10;

    if let Some(x) = vals.get_mut(1) {
        *x *= 10;
    }

    if let Some(last) = vals.last_mut() {
        *last = 50;
    }

    vals.swap(2, 3);
    println!("{vals:?}");

    for x in vals.iter_mut() {
        *x += 1;
    }
    println!("{vals:?}");

    vals.reverse();
    println!("{vals:?}");

    vals.fill(0);
    println!("{vals:?}");
}

We change single elements and then all elements at once.

vals[0] = 10;

if let Some(x) = vals.get_mut(1) {
    *x *= 10;
}

The get_mut method returns a mutable reference to the element. We modify the value through the reference with the dereference operator *.

vals.swap(2, 3);

The swap method exchanges two elements.

for x in vals.iter_mut() {
    *x += 1;
}

The iter_mut method returns an iterator of mutable references.

vals.reverse();
vals.fill(0);

The reverse method reverses the order of the elements in place; fill sets all elements to a value.

$ cargo run -q
[10, 20, 4, 3, 50]
[11, 21, 5, 4, 51]
[51, 4, 5, 21, 11]
[0, 0, 0, 0, 0]

Adding elements

Elements are added to the end with push, at a given position with insert, and from other collections with extend, append, and extend_from_slice.

main.rs
fn main() {
    let mut scores = vec![10, 20];

    scores.push(30);
    println!("{scores:?}");

    scores.insert(1, 15);
    println!("{scores:?}");

    scores.extend([40, 50]);
    println!("{scores:?}");

    let mut more = vec![60, 70];
    scores.append(&mut more);
    println!("{scores:?} {more:?}");

    scores.extend_from_slice(&[80, 90]);
    println!("{scores:?}");
}

We add elements to a vector of scores.

scores.insert(1, 15);

The insert method shifts all following elements to the right, so it takes time proportional to the length of the vector. Adding to the end with push is fast.

scores.extend([40, 50]);

The extend method adds the elements of any iterable.

let mut more = vec![60, 70];
scores.append(&mut more);

The append method moves all elements of another vector, which is left empty.

scores.extend_from_slice(&[80, 90]);

The extend_from_slice method clones the elements of a slice.

$ cargo run -q
[10, 20, 30]
[10, 15, 20, 30]
[10, 15, 20, 30, 40, 50]
[10, 15, 20, 30, 40, 50, 60, 70] []
[10, 15, 20, 30, 40, 50, 60, 70, 80, 90]

Removing elements

Vectors provide several removal methods: pop for the last element, remove for any index, swap_remove for fast removal when the order does not matter, and truncate and clear for many elements.

main.rs
fn main() {
    let mut items = vec!["A", "B", "C", "D", "E", "F"];

    let last = items.pop();
    println!("popped: {last:?}, {items:?}");

    let removed = items.remove(1);
    println!("removed: {removed}, {items:?}");

    let swapped = items.swap_remove(0);
    println!("swap_removed: {swapped}, {items:?}");

    items.truncate(2);
    println!("truncated: {items:?}");

    items.clear();
    println!("cleared: {items:?}, capacity {}", items.capacity());

    let mut empty: Vec<i32> = Vec::new();
    println!("{:?}", empty.pop());
}

We remove elements in several ways.

let last = items.pop();

The pop method removes the last element and returns it in an Option; for an empty vector it returns None.

let removed = items.remove(1);

The remove method returns the element and shifts the following elements to the left.

let swapped = items.swap_remove(0);

The swap_remove method replaces the removed element with the last one. It runs in constant time, but changes the order of the elements: "E" moved to the front.

items.truncate(2);
items.clear();

The truncate method keeps only the first elements; clear removes all of them. Neither method frees the allocated memory; the capacity remains the same.

$ cargo run -q
popped: Some("F"), ["A", "B", "C", "D", "E"]
removed: B, ["A", "C", "D", "E"]
swap_removed: A, ["E", "C", "D"]
truncated: ["E", "C"]
cleared: [], capacity 6
None

Filtering in place

The retain, dedup, drain, split_off, and extract_if methods remove groups of elements.

main.rs
fn main() {
    let mut vals = vec![1, 2, 2, 3, 3, 3, 4, 5, 5, 6];

    vals.dedup();
    println!("dedup: {vals:?}");

    vals.retain(|&x| x % 2 == 0);
    println!("retain: {vals:?}");

    let mut words = vec!["sky", "Sky", "rock", "ROCK", "rock", "sun"];
    words.dedup_by_key(|w| w.to_lowercase());
    println!("{words:?}");

    let mut nums: Vec<i32> = (1..=10).collect();
    let drained: Vec<i32> = nums.drain(2..5).collect();
    println!("drained: {drained:?}, left: {nums:?}");

    let tail = nums.split_off(4);
    println!("{nums:?} {tail:?}");

    let mut data = vec![5, 1, 4, 1, 5, 9, 2, 6];
    let small: Vec<i32> = data.extract_if(.., |x| *x < 4).collect();
    println!("{small:?} {data:?}");
}

We remove duplicates and filter the elements of vectors.

vals.dedup();
vals.retain(|&x| x % 2 == 0);

The dedup method removes consecutive duplicates; to remove all duplicates, sort the vector first. The retain method keeps only the elements for which the closure returns true.

words.dedup_by_key(|w| w.to_lowercase());

The dedup_by_key method compares the keys computed by the closure. Consecutive words that differ only in case are treated as duplicates.

let drained: Vec<i32> = nums.drain(2..5).collect();
let tail = nums.split_off(4);

The drain method removes a range and returns the removed elements as an iterator. The split_off method splits the vector at an index and returns the second part as a new vector.

let small: Vec<i32> = data.extract_if(.., |x| *x < 4).collect();

The extract_if method removes the elements that match the closure and returns them; the rest stay in the vector. It combines retain with collecting the removed elements.

$ cargo run -q
dedup: [1, 2, 3, 4, 5, 6]
retain: [2, 4, 6]
["sky", "rock", "sun"]
drained: [3, 4, 5], left: [1, 2, 6, 7, 8, 9, 10]
[1, 2, 6, 7] [8, 9, 10]
[1, 1, 2] [5, 4, 5, 9, 6]

Iterating over vectors

A vector can be iterated over by shared references, by mutable references, or by value. Iterating by value consumes the vector.

main.rs
fn main() {
    let mut numbers = vec![1, 2, 3, 4, 5];

    for n in &numbers {
        print!("{n} ");
    }
    println!();

    for n in &mut numbers {
        *n *= 2;
    }
    println!("{numbers:?}");

    for (i, n) in numbers.iter().enumerate() {
        print!("{i}:{n} ");
    }
    println!();

    let strings = vec![String::from("Hello"), String::from("World")];
    for s in strings {
        println!("consumed: {s}");
    }
}

We go over the elements in the three ways.

for n in &numbers {
    print!("{n} ");
}

Iterating over &numbers borrows the vector; the loop variable is a &i32. The vector can be used after the loop.

for n in &mut numbers {
    *n *= 2;
}

Iterating over &mut numbers gives mutable references, through which we change the elements.

for (i, n) in numbers.iter().enumerate() {

The enumerate adapter adds the index to each element.

for s in strings {
    println!("consumed: {s}");
}

Without a reference, the loop takes ownership of the vector and moves the strings out of it. The strings variable cannot be used after the loop.

$ cargo run -q
1 2 3 4 5 
[2, 4, 6, 8, 10]
0:2 1:4 2:6 3:8 4:10 
consumed: Hello
consumed: World

Borrowing rules

While a reference to an element exists, the vector cannot be modified. A modification may reallocate the buffer, which would leave the reference pointing to freed memory.

main.rs
fn main() {
    let mut vals = vec![1, 2, 3];

    let first = &vals[0];
    vals.push(4);

    println!("{first}");
}

We take a reference to the first element and then push a new element.

let first = &vals[0];
vals.push(4);

println!("{first}");

The push method needs a mutable borrow of the vector, but the shared borrow in first is still used later. The compiler reports the error E0502. Moving the println! before the push, or copying the value with let first = vals[0];, fixes the code.

$ cargo run -q
error[E0502]: cannot borrow `vals` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:5
  |
4 |     let first = &vals[0];
  |                  ---- immutable borrow occurs here
5 |     vals.push(4);
  |     ^^^^^^^^^^^^ mutable borrow occurs here
6 |
7 |     println!("{first}");
  |                ----- immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.

Capacity and length

The length is the number of elements; the capacity is the number of elements that fit into the allocated buffer.

main.rs
fn main() {
    let mut v = Vec::with_capacity(3);
    println!("initial: len {}, cap {}", v.len(), v.capacity());

    v.push(1);
    v.push(2);
    v.push(3);
    println!("full: len {}, cap {}", v.len(), v.capacity());

    v.push(4);
    println!("grown: len {}, cap {}", v.len(), v.capacity());

    v.shrink_to_fit();
    println!("shrunk: len {}, cap {}", v.len(), v.capacity());

    v.reserve(10);
    println!("reserved: len {}, cap {}", v.len(), v.capacity());

    let mut w = Vec::new();
    let mut caps = vec![w.capacity()];
    for i in 0..100 {
        w.push(i);
        if w.capacity() != *caps.last().unwrap() {
            caps.push(w.capacity());
        }
    }
    println!("{caps:?}");
}

We watch how the capacity changes when the vector grows.

v.push(4);
println!("grown: len {}, cap {}", v.len(), v.capacity());

The fourth element does not fit into the buffer, so the vector reallocates. The new capacity is twice the old one.

v.shrink_to_fit();
v.reserve(10);

The shrink_to_fit method reduces the capacity to the length; reserve makes room for at least ten more elements.

let mut w = Vec::new();
let mut caps = vec![w.capacity()];
for i in 0..100 {
    w.push(i);
    if w.capacity() != *caps.last().unwrap() {
        caps.push(w.capacity());
    }
}

We record the capacity each time it changes. For elements of this size, the first allocation has room for four elements, and then the capacity doubles. Doubling makes push fast on average: over a hundred pushes, the vector allocates a buffer only six times. The exact growth strategy is not guaranteed and may change.

$ cargo run -q
initial: len 0, cap 3
full: len 3, cap 3
grown: len 4, cap 6
shrunk: len 4, cap 4
reserved: len 4, cap 14
[0, 4, 8, 16, 32, 64, 128]

Slices

A slice, &[T], is a view into a contiguous part of a vector or an array. Slices are created with ranges and do not copy the data.

main.rs
fn sum(vals: &[i32]) -> i32 {
    vals.iter().sum()
}

fn main() {
    let data = vec![10, 20, 30, 40, 50, 60];

    let first_half = &data[..3];
    let middle = &data[1..4];
    let last = &data[4..];

    println!("{first_half:?} {middle:?} {last:?}");
    println!("sum of all: {}", sum(&data));
    println!("sum of a part: {}", sum(&data[2..5]));
    println!("sum of an array: {}", sum(&[1, 2, 3]));

    for chunk in data.chunks(4) {
        println!("chunk: {chunk:?}");
    }

    let diffs: Vec<i32> = data.windows(2).map(|w| w[1] - w[0]).collect();
    println!("diffs: {diffs:?}");

    let (left, right) = data.split_at(2);
    println!("{left:?} {right:?}");
}

We create slices of a vector and pass them to a function.

fn sum(vals: &[i32]) -> i32 {
    vals.iter().sum()
}

A function that reads a sequence should take a slice instead of &Vec<T>. Then it accepts a whole vector, a part of it, and an array as well. A &Vec<i32> is converted to &[i32] automatically.

for chunk in data.chunks(4) {
    println!("chunk: {chunk:?}");
}

The chunks method splits the slice into parts of the given size; the last part may be shorter.

let diffs: Vec<i32> = data.windows(2).map(|w| w[1] - w[0]).collect();

The windows method returns overlapping slices. We compute the differences between neighbouring elements. See the Rust slices article for more slice methods.

$ cargo run -q
[10, 20, 30] [20, 30, 40] [50, 60]
sum of all: 210
sum of a part: 120
sum of an array: 6
chunk: [10, 20, 30, 40]
chunk: [50, 60]
diffs: [10, 10, 10, 10, 10]
[10, 20] [30, 40, 50, 60]

Sorting and searching

Vectors are sorted in place with sort and sort_by. A sorted vector can be searched with binary_search.

main.rs
fn main() {
    let mut nums = vec![42, 7, 19, 3, 99, 15];

    nums.sort();
    println!("sorted: {nums:?}");

    match nums.binary_search(&19) {
        Ok(idx) => println!("found 19 at index {idx}"),
        Err(_) => println!("19 not found"),
    }

    if let Err(idx) = nums.binary_search(&20) {
        nums.insert(idx, 20);
    }
    println!("with 20: {nums:?}");

    nums.sort_by(|a, b| b.cmp(a));
    println!("descending: {nums:?}");

    println!("contains 42? {}", nums.contains(&42));
    println!("position of 7: {:?}", nums.iter().position(|&x| x == 7));

    let mut temps: Vec<f64> = vec![22.5, -3.1, 17.0, 8.4];
    temps.sort_by(|a, b| a.total_cmp(b));
    println!("{temps:?}");
}

We sort numbers, search them, and keep the vector sorted when inserting.

match nums.binary_search(&19) {
    Ok(idx) => println!("found 19 at index {idx}"),
    Err(_) => println!("19 not found"),
}

Binary search works only on sorted data. It returns Ok with the index of the found element.

if let Err(idx) = nums.binary_search(&20) {
    nums.insert(idx, 20);
}

For a missing value, binary_search returns Err with the position where the value can be inserted to keep the order.

nums.sort_by(|a, b| b.cmp(a));

The sort_by method takes a comparison function. Swapping the arguments sorts in descending order. The contains and position methods search linearly and work on unsorted data.

let mut temps: Vec<f64> = vec![22.5, -3.1, 17.0, 8.4];
temps.sort_by(|a, b| a.total_cmp(b));

Floats do not implement Ord, because NaN is not comparable, so sort is not available. The total_cmp method defines a total order for floats.

$ cargo run -q
sorted: [3, 7, 15, 19, 42, 99]
found 19 at index 3
with 20: [3, 7, 15, 19, 20, 42, 99]
descending: [99, 42, 20, 19, 15, 7, 3]
contains 42? true
position of 7: Some(5)
[-3.1, 8.4, 17.0, 22.5]

Sorting structures

Vectors of structures are sorted by a key or with a custom comparison.

main.rs
#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let mut users = vec![
        User { name: String::from("Paul"), age: 33 },
        User { name: String::from("Anna"), age: 24 },
        User { name: String::from("Robert"), age: 33 },
        User { name: String::from("Lucy"), age: 19 },
    ];

    users.sort_by_key(|u| u.age);
    for u in &users {
        println!("{} {}", u.name, u.age);
    }
    println!();

    users.sort_by(|a, b| b.age.cmp(&a.age).then_with(|| a.name.cmp(&b.name)));
    for u in &users {
        println!("{} {}", u.name, u.age);
    }

    let oldest = users.iter().max_by_key(|u| u.age).unwrap();
    println!("{oldest:?}");
}

We sort users by age and then by age in descending order and by name.

users.sort_by_key(|u| u.age);

The sort_by_key method sorts by the value returned by the closure. The sort is stable: Paul and Robert have the same age and keep their original order.

users.sort_by(|a, b| b.age.cmp(&a.age).then_with(|| a.name.cmp(&b.name)));

The then_with method of Ordering adds a secondary key: users with the same age are sorted by name.

let oldest = users.iter().max_by_key(|u| u.age).unwrap();

When several elements have the maximum key, max_by_key returns the last of them, here Robert.

$ cargo run -q
Lucy 19
Anna 24
Paul 33
Robert 33

Paul 33
Robert 33
Anna 24
Lucy 19
User { name: "Robert", age: 33 }

Transforming vectors

Iterator methods like map, filter, and collect create new vectors from existing ones.

main.rs
fn main() {
    let numbers: Vec<i32> = (1..=10).collect();

    let even_squares: Vec<i32> = numbers
        .iter()
        .filter(|&x| x % 2 == 0)
        .map(|x| x * x)
        .collect();
    println!("{even_squares:?}");

    let labels: Vec<String> = numbers.iter().take(3).map(|n| format!("#{n}")).collect();
    println!("{labels:?}");

    let (even, odd): (Vec<i32>, Vec<i32>) = numbers.iter().partition(|&x| x % 2 == 0);
    println!("{even:?} {odd:?}");

    let total: i32 = numbers.iter().sum();
    let max = numbers.iter().max();
    let avg = total as f64 / numbers.len() as f64;
    println!("sum {total}, max {max:?}, average {avg}");

    let words: Vec<String> = numbers.iter().take(4).map(|n| n.to_string()).collect();
    println!("{}", words.join(", "));
    println!("{:?}", [vec![1, 2], vec![3], vec![4, 5]].concat());
}

We transform and aggregate a vector of numbers.

let even_squares: Vec<i32> = numbers
    .iter()
    .filter(|&x| x % 2 == 0)
    .map(|x| x * x)
    .collect();

We keep the even numbers and square them. The iterator chain is compiled into a simple loop; no intermediate vectors are created.

let (even, odd): (Vec<i32>, Vec<i32>) = numbers.iter().partition(|&x| x % 2 == 0);

The partition method splits the elements into two vectors.

let total: i32 = numbers.iter().sum();
let max = numbers.iter().max();

The sum method needs a type annotation. The max method returns an Option, because an empty vector has no maximum.

println!("{}", words.join(", "));
println!("{:?}", [vec![1, 2], vec![3], vec![4, 5]].concat());

The join method joins strings with a separator; concat joins vectors into one.

$ cargo run -q
[4, 16, 36, 64, 100]
["#1", "#2", "#3"]
[2, 4, 6, 8, 10] [1, 3, 5, 7, 9]
sum 55, max Some(10), average 5.5
1, 2, 3, 4
[1, 2, 3, 4, 5]

Vectors of vectors

A vector of vectors represents a two-dimensional grid, whose rows can have different lengths.

main.rs
fn main() {
    let rows = 3;
    let cols = 4;

    let mut grid = vec![vec![0; cols]; rows];

    for (r, row) in grid.iter_mut().enumerate() {
        for (c, cell) in row.iter_mut().enumerate() {
            *cell = r * cols + c;
        }
    }

    for row in &grid {
        println!("{row:?}");
    }

    println!("grid[1][2] = {}", grid[1][2]);

    let col_sums: Vec<usize> = (0..cols).map(|c| grid.iter().map(|row| row[c]).sum()).collect();
    println!("column sums: {col_sums:?}");

    let flat: Vec<usize> = grid.into_iter().flatten().collect();
    println!("{flat:?}");
}

We create a grid of three rows and four columns and fill it with numbers.

let mut grid = vec![vec![0; cols]; rows];

The inner vector is cloned for each row. Each row is a separate heap allocation.

for (r, row) in grid.iter_mut().enumerate() {
    for (c, cell) in row.iter_mut().enumerate() {
        *cell = r * cols + c;
    }
}

Nested loops with iter_mut modify the cells.

let col_sums: Vec<usize> = (0..cols).map(|c| grid.iter().map(|row| row[c]).sum()).collect();

To sum a column, we take the element with the same index from every row.

let flat: Vec<usize> = grid.into_iter().flatten().collect();

The flatten adapter joins the rows into a single vector. For large grids of a fixed size, a single vector with r * cols + c indexing is more efficient.

$ cargo run -q
[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
grid[1][2] = 6
column sums: [12, 15, 18, 21]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

Comparing vectors

Vectors can be compared with the comparison operators if their elements can be compared.

main.rs
fn main() {
    let a = vec![1, 2, 3];
    let b = vec![1, 2, 3];
    let c = vec![1, 2, 4];

    println!("{}", a == b);
    println!("{}", a == [1, 2, 3]);
    println!("{}", a < c);
    println!("{}", vec![1, 2] < vec![1, 2, 0]);

    let mut copy = a.clone();
    copy.push(4);
    println!("{a:?} {copy:?}");

    println!("{}", a.starts_with(&[1, 2]));
    println!("{}", copy.ends_with(&[3, 4]));
    println!("{}", a.is_empty());
}

We compare vectors and check their beginnings and ends.

println!("{}", a == b);
println!("{}", a == [1, 2, 3]);

Two vectors are equal if they have the same elements in the same order. A vector can also be compared with an array.

println!("{}", a < c);
println!("{}", vec![1, 2] < vec![1, 2, 0]);

The comparison is lexicographic: the first differing element decides; a shorter vector that is a prefix of a longer one is smaller.

let mut copy = a.clone();
copy.push(4);

The clone method creates an independent copy with its own buffer.

$ cargo run -q
true
true
true
true
[1, 2, 3] [1, 2, 3, 4]
true
true
false

Source

The Vec type

The Rust Programming Language: Storing Lists of Values with Vectors

In this article we have worked with vectors in Rust.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Rust tutorials.