Vec. In Rust programs we use vectors to store values in an efficient way. Programs often create vectors in many places, and many times.
With many syntax forms, we can create vectors in an elegant and concise way in Rust programs. We can repeat values, and even use a capacity to speed up later push() calls.
First example. This Rust program initializes vectors in 7 different ways. We see the "vec!" macro, which can be empty, have element values, or a repeated element value.
Info To set a capacity, use with_capacity—this will provide memory for the specified number of elements to be added with no resizing.
Next We can use extend_from_slice to append many elements from a slice at once (another vector can be appended as well).
Finally We can initialize a vector by calling the from() function to copy its initial values.
fn main() {
// Empty vector.
let mut values = vec![];
values.push(5);
println!("{:?}", values);
// Vector with 3 values.
let values = vec![10, 20, 30];
println!("{:?}", values);
// Vector with repeated value.
let values = vec![1; 2];
println!("{:?}", values);
// Vector with capacity.
let mut values = Vec::with_capacity(100);
values.push(3);
println!("{:?}", values);
// Empty vector, extend from slice.
let mut values = Vec::new();
values.extend_from_slice(&[1, 2, 3]);
println!("{:?}", values);
// Vector from.
let values = Vec::from([4, 5]);
println!("{:?}", values);
}[5]
[10, 20, 30]
[1, 1]
[3]
[1, 2, 3]
[4, 5]
Push, remove. The push() function adds an element onto the end of the vector. It is important that our vector be a mutable (mut) variable for calling push.
Also Remove() removes by index. So a call to remove "1" will remove the element at index 1—the second element.
fn main() {
let mut values = vec![1, 2];
println!("{:?}", values);
// Add value to end.
values.push(3);
println!("{:?}", values);
// Remove value with index 1.
values.remove(1);
println!("{:?}", values);
}[1, 2]
[1, 2, 3]
[1, 3]
Loop example. Sometimes we need to initialize vectors in a loop or assign elements by index directly. We must make sure to use the "mut" keyword to have a mutable vector.
fn main() {
// Create all-zero vector, then assign elements into vector.
let mut items = vec![0; 10];
items[0] = -2;
items[5] = 10;
items[9] = -1;
// Print with for-loop.
for item in &items {
println!("ITEM: {}", item);
}
// Modify vector by index.
for i in 0..items.len() {
items[i] = items[i] * 2;
}
// Print with loop again.
for item in &items {
println!("MULTIPLIED: {}", item);
}
}ITEM: -2
ITEM: 0
ITEM: 0
ITEM: 0
ITEM: 0
ITEM: 10
ITEM: 0
ITEM: 0
ITEM: 0
ITEM: -1
MULTIPLIED: -4
MULTIPLIED: 0
MULTIPLIED: 0
MULTIPLIED: 0
MULTIPLIED: 0
MULTIPLIED: 20
MULTIPLIED: 0
MULTIPLIED: 0
MULTIPLIED: 0
MULTIPLIED: -2
Stack. There is no special stack type in Rust—the Vec doubles as a stack. We can use a "while let" loop to pop each element until no more elements are present.
Here We call map() on an iterator, and then collect the results into another Vector of integers.
fn main() {
let values = vec![1, 2, 3];
// Use collect to convert from an iterator to a vector.
let result = values.iter().map(|x| x * 2).collect::<Vec<i32>>();
println!("{:?}", result);
}[2, 4, 6]
IsEmpty. It is possible to test the element count of a vector with len(). The clippy tool will recommend using is_empty() instead of testing against a zero length.
fn main() {
let mut v = vec![];
// Vector is currently empty.
if v.is_empty() {
println!("EMPTY");
}
// Add an element.
v.push(10);
// No longer empty.
if !v.is_empty() {
println!("NOT EMPTY");
}
}EMPTY
NOT EMPTY
Swap. Sometimes we want to exchange the positions of 2 elements in a vector. The swap() method can do this—and it tends to be a clear way of expressing this logic.
fn main() {
// Use swap to change the positions of the vector elements.
let mut rat = vec!['r', 'a', 't'];
println!("{:?}", rat);
rat.swap(0, 2);
println!("{:?}", rat);
}['r', 'a', 't']
['t', 'a', 'r']
Capacity benchmark. A significant speedup can be attained by using the with_capacity() function to create a vector in Rust. We test vector creation here.
Version 1 This version of the code creates many 100-element vectors by first using the with_capacity() function with an accurate capacity of 100.
Version 2 Here we just create empty vectors, and then add 100 elements to them. So the vector must resize on its own.
Result Using with_capacity() can give a nice speedup for vector creation, and is always recommended if any idea about the capacity is known.
use std::time::*;
fn main() {
if let Ok(max) = "10000000".parse::<usize>() {
// Version 1: use with_capacity.
let t0 = Instant::now();
for _ in 0..max {
let mut v = Vec::with_capacity(100);
for i in 0..100 {
v.push(i);
}
}
println!("{}", t0.elapsed().as_millis());
// Version 2: use new.
let t1 = Instant::now();
for _ in 0..max {
let mut v = Vec::new();
for i in 0..100 {
v.push(i);
}
}
println!("{}", t1.elapsed().as_millis());
}
} 916 (with_capacity)
2645 (new)
Slice loop optimization. How can we optimize looping over elements in a vector? It is important to loop up to the len() of a vector—this helps the compiler optimize better.
Version 1 We loop over the original array directly, up to 2 elements before the end (the last 2 elements are not reached).
Version 2 We create a slice of the vector that does not include the last 2 elements, and then loop over the entire slice up to its length.
Result The second version is faster, as it allows the compiler to optimize out bounds checks. It helps to go up to a vectors len() in loops.
Tip To loop over only part of a vector, create a slice of just those elements, and loop over the entire slice using len() as a top bounds.
use std::time::*;
fn main() {
if let Ok(max_initial) = "10000".parse::<usize>() {
// Data for test.
let mut data = Vec::with_capacity(max_initial);
for i in 0..max_initial {
data.push(i);
}
let mut count1 = 0;
let mut count2 = 0;
// Version 1: loop over vec except last 2 elements.
let t0 = Instant::now();
for _ in 0..100000 {
let max = data.len() - 2;
for i in 0..max {
if data[i] == 0 {
count1 += 1;
}
}
}
println!("{}", t0.elapsed().as_millis());
// Version 2: create slice without last 2 elements, and loop over it up to len.
let t1 = Instant::now();
for _ in 0..100000 {
let slice = &data[0..data.len() - 2];
for i in 0..slice.len() {
if slice[i] == 0 {
count2 += 1;
}
}
}
println!("{}", t1.elapsed().as_millis());
println!("{}/{}", count1, count2);
}
}177 0..max
156 0..slice.len()
100000/100000
Nested vector. It is possible to create 2D vectors, and use even further dimensions, by nesting vectors. This is like a "jagged vector."
Get unchecked. It is possible to access a vector element without bounds checking for a speedup. But if possible, try using iter() or slice-based loops for the same effect in safe code.
We can create and initialize Vecs in many ways in Rust. The macro "vec!" is most often used, but the other functions like with_capacity() are also important.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on May 11, 2023 (new example).