Reading an element
A Vec gives you its elements by position, using square brackets after its name, exactly as an array does. Positions start at zero: the first element is at index 0, the second at 1, and a vector with n elements has its last element at index n - 1.
let array = vec![50, 60, 70];
println!("{}", array[0]); // 50
let data = array[1];
println!("{}", data); // 60
The expression array[1] has the value of the element, so it can be printed, stored in a variable, added to another value or passed to a function. The index itself must be a usize. A literal such as 1 is inferred as one, while an i32 variable must be converted with as usize before it can index.
Indexing and ownership
Indexing with [] reads the element in place. For simple types like integers and for string slices (&str), the value is copied, and you get your own copy. Those types are cheap to copy, and Rust does it for you. For a vector of String, though, let s = names[0]; won't compile, because moving a String out of a vector would leave a hole. Borrow it with &names[0] or clone it with names[0].clone().
The lesson's exercise
The starting code has two vectors:
let fruits = vec!["mango", "kiwi", "grapefruit"];
let ages = vec![14, 32, 50, 21];
You print three things, each with a println! call. Its first argument is a format string, where each {} is replaced by the next argument.
- The second fruit. Zero-based indexing puts it at index
1:println!("{}", fruits[1]);printskiwi. Index2would be the third fruit. - The sum of the first three ages. Index
0,1and2hold 14, 32 and 50. The expression is evaluated before printing, so the call receives the finished sum,96. - The last age minus the first. With four ages, the last is at index
3, and 21 - 14 is7.
let fruits = vec!["mango", "kiwi", "grapefruit"];
let ages = vec![14, 32, 50, 21];
println!("{}", fruits[1]);
println!("{}", ages[0] + ages[1] + ages[2]);
println!("{}", ages[3] - ages[0]);
The output is kiwi, 96 and 7, one per line.
The last element and safe access
Because a vector can change size, the last index isn't a fixed number in general. Compute it from the length: ages[ages.len() - 1] is the last age, 21. The length is a usize, which can't go below zero. For an empty vector, len() - 1 underflows. In the optimized build the lessons use it wraps to a huge number, and in a debug build it panics. Either way the program fails.
Two methods avoid that and avoid panics, because they return an Option:
get(i)givesSome(&element)when the index exists andNoneotherwise. Forvec![1, 2, 3],get(1)isSome(2)andget(10)isNone.last()gives the final element, orNonefor an empty vector.
Plain indexing is right when you know the index is valid, as it is in this exercise. Prefer get when the index comes from data you don't control.
Out-of-range indexes
Rust checks every index when the program runs. If the index is past the end, the program panics with a message such as index out of bounds: the len is 3 but the index is 5. It never reads memory outside the vector. Negative indexes don't exist: ages[-1] is a compile error, since the index must be unsigned.
Common mistakes
- Off-by-one. The second item is at index 1, and the item at
len()doesn't exist. - Indexing with an
i32. The compiler reports that the vector can't be indexed byi32. Use ausize, or convert deliberately withas usize. - Forgetting the format string.
println!(fruits[1])is an error. The first argument must be a literal such as"{}". - Printing a whole vector with
{}. Use{:?}for the full vector, which prints[14, 32, 50, 21].
Key takeaways
v[i]reads the element at positioni; the first position is 0 and the last islen() - 1.- Indexes have type
usize, and an out-of-range index panics. - Use
get(i)orlast()when the element might not exist. - An indexed element is an ordinary value you can add, compare or print.
