Vectors
Prerequisites: Tuples & arrays
A vector, Vec<T>, is a growable list of values of the same type
T, stored on the heap. It's the collection you'll reach for most
often.
Syntax
let mut names: Vec<String> = Vec::new(); // empty; type must be known
let mut numbers = vec![1, 2, 3]; // `vec!` macro with values
numbers.push(4); // add to the end
let third = numbers[2]; // index: panics if out of range
let maybe = numbers.get(10); // `get`: returns Some(&value) or None
numbers.len(); // number of items
Vec::new()creates an empty vector. Rust needs to know the element type, either from an annotation or from what you push later.vec![...]creates a vector with initial values.pushadds to the end;popremoves the last item;insert(i, x)andremove(i)work at a position.- Indexing with
v[i]panics ifiis out of range. get(i)returns anOption:Some(&value)if the index exists,Noneif it doesn't. It's the safe choice when the index might be invalid. Handle it withmatch(the Enums & match lesson coversOptionproperly).- Loop with
for x in &vto read each item, orfor x in &mut vto change each one (write*xto reach the value behind the reference).for x in vwithout&moves the vector into the loop.
Example
fn main() {
let mut numbers = vec![10, 20, 30];
numbers.push(40);
println!("{numbers:?} has {} items", numbers.len());
println!("numbers[1] = {}", numbers[1]);
match numbers.get(10) {
Some(n) => println!("found {n}"),
None => println!("no item at index 10"),
}
for n in &mut numbers {
*n *= 2;
}
for n in &numbers {
print!("[{n}]");
}
println!();
numbers.remove(0);
numbers.insert(0, 5);
println!("{numbers:?}");
println!("contains 60? {}", numbers.contains(&60));
let mut words: Vec<String> = Vec::new();
words.push(String::from("pear"));
words.push(String::from("apple"));
words.push(String::from("fig"));
words.sort();
println!("{words:?}");
if let Some(last) = words.pop() {
println!("popped {last}, {} left", words.len());
}
}
if let is a shorter form of match for when you only care about one
case; it's explained in the Enums & match lesson.
Expected output
[10, 20, 30, 40] has 4 items
numbers[1] = 20
no item at index 10
[20][40][60][80]
[5, 40, 60, 80]
contains 60? true
["apple", "fig", "pear"]
popped pear, 2 left
Common mistakes
- Holding a reference while changing the vector:
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);
println!("{first}");
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
This looks harmless, but push may move the whole vector to a
bigger block of memory, which would leave first pointing at freed
memory. Rust refuses to compile it.
- Indexing out of range at runtime panics with
index out of bounds: the len is 3 but the index is 10. Use.get()when the index might not exist. - Writing
for n in numbersand then trying to usenumbersagain. Without&, the loop takes ownership of the vector.
Practice
Create an empty Vec<i32>, push every even number from 1 to 10 into
it with a loop, then print the vector and the sum of its items:
[2, 4, 6, 8, 10]
Sum: 30
Show solution
fn main() {
let mut evens: Vec<i32> = Vec::new();
for n in 1..=10 {
if n % 2 == 0 {
evens.push(n);
}
}
let mut sum = 0;
for n in &evens {
sum += n;
}
println!("{evens:?}");
println!("Sum: {sum}");
}