Vec in Rust: creating, adding, removing and iterating
A practical guide to Rust's Vec<T>: vec!, push and pop, get vs indexing, insert and remove, sort, retain, dedup, and with_capacity.
Vec<T> is Rust's growable array: a list of values of the same type, stored next to each other on the heap. It's the collection you'll use most.
Creating
let mut scores: Vec<u32> = Vec::new(); // empty, type given
let names = vec!["Ferris", "Corro"]; // with values, type inferred
let zeros = vec![0; 10]; // ten zeros
let mut buf = Vec::with_capacity(1000); // room for 1000 before reallocating
Reading
v[i]returns the element, and panics ifiis out of range:index out of bounds: the len is 3 but the index is 3.v.get(i)returns anOption<&T>:Noneinstead of a panic. Use it when the index comes from outside (user input, a file).v.first(),v.last(),v.len(),v.is_empty(),v.contains(&x),v.iter().position(|x| ...).
Adding and removing
| Method | Does | Cost |
|---|---|---|
push(x) |
add at the end | fast (amortized O(1)) |
pop() |
remove the last, returns Option<T> |
fast |
insert(i, x) |
add at index i, shifting the rest right |
O(n) |
remove(i) |
take out index i, shifting the rest left |
O(n) |
swap_remove(i) |
take out index i, filling the gap with the last |
fast, changes order |
truncate(n), clear() |
drop elements past n / all of them |
|
extend(other) |
append everything from an iterator | |
retain(|x| ...) |
keep only elements that pass a test | O(n) |
dedup() |
remove consecutive duplicates (sort first) | O(n) |
Sorting
sort() for types with a natural order (integers, strings). sort_by_key(|p| p.age) to sort by a field, sort_by(|a, b| b.score.cmp(&a.score)) for full control (this one sorts descending). f64 has no total order, so sort() on floats doesn't compile; use sort_by(|a, b| a.total_cmp(b)). sort_unstable* variants are a bit faster when equal elements don't need to keep their order.
Iterating
for x in &v: borrow each element (&T);vis still usable afterwards.for x in &mut v: change elements in place (*x += 1).for x in v: consume the vector, taking each element by value.
Capacity
A Vec has a length (elements in use) and a capacity (room reserved). When push exceeds the capacity, it allocates a bigger block (roughly double) and moves everything. That's why holding a reference into a Vec while pushing is forbidden (E0502), and why with_capacity helps when you know the size up front.
Common mistakes
- Indexing with the wrong integer type. Indexes are
usize:
fn main() {
let v = vec![10, 20, 30];
let i: i32 = 1;
println!("{}", v[i]);
}
error[E0277]: the type `[{integer}]` cannot be indexed by `i32`
Convert with v[i as usize] (if i can't be negative) or use usize for the variable.
v.len() - 1on an empty vector panics (attempt to subtract with overflow). Usev.last().- Moving out with an index:
let s = names[0];for aVec<String>is E0507. Borrow (&names[0]) orclone(). - Removing while iterating: use
retaininstead of a loop withremove.
Example
#[derive(Debug)]
struct Item {
name: &'static str,
qty: u32,
}
fn main() {
let mut inventory = vec![
Item { name: "sword", qty: 1 },
Item { name: "potion", qty: 5 },
Item { name: "arrow", qty: 0 },
Item { name: "shield", qty: 2 },
];
inventory.push(Item { name: "map", qty: 1 });
println!("{} kinds of item", inventory.len());
inventory.retain(|it| it.qty > 0);
inventory.sort_by_key(|it| it.name);
let names: Vec<&str> = inventory.iter().map(|it| it.name).collect();
println!("{names:?}");
for it in &mut inventory {
it.qty *= 2;
}
println!("potions: {:?}", inventory.iter().find(|it| it.name == "potion").map(|it| it.qty));
println!("index 1: {:?}", inventory.get(1));
println!("index 9: {:?}", inventory.get(9));
let last = inventory.pop();
println!("popped {last:?}, {} left", inventory.len());
let mut readings: Vec<f64> = vec![3.5, -1.0, 2.25, 9.0];
readings.sort_by(|a, b| a.total_cmp(b));
println!("{readings:?}");
let mut tags = vec!["b", "a", "b", "c", "a"];
tags.sort();
tags.dedup();
println!("{tags:?}");
}
Output:
5 kinds of item
["map", "potion", "shield", "sword"]
potions: Some(10)
index 1: Some(Item { name: "potion", qty: 10 })
index 9: None
popped Some(Item { name: "sword", qty: 2 }), 3 left
[-1.0, 2.25, 3.5, 9.0]
["a", "b", "c"]