Home
Rust
vec Examples
This page was last reviewed on May 11, 2023.
Dot Net Perls
Vec. In Rust programs we use vectors to store values in an efficient way. Programs often create vectors in many places, and many times.
Shows a vec
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).
Vec extend from slice
Finally We can initialize a vector by calling the from() function to copy its initial values.
Shows a vec
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.
Vec 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.
mut
Here We use for-loops on the vector. We must make sure to borrow the vector to allow it to be used again later.
for
Cannot Borrow Error
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.
if
Info Pop() takes the last element in the Vec, removes it, and returns it. If the Vec is empty, None is returned.
fn main() { let mut stack = vec![]; stack.push(1); stack.push(2); // Use Vec as a Stack. while let Some(item) = stack.pop() { println!("STACK ITEM: {}", item); } }
STACK ITEM: 2 STACK ITEM: 1
Return vector. Suppose we want to place the logic for vector initialization in a separate function. We can do return a Vec from a function.
Here The get_new_vector() function returns a vector of 2 strings. We can use it to get that vector whenever we want it.
fn get_new_vector() -> Vec<String> { // Return a vector from a function. vec!["a".to_string(), "b".to_string()] } fn main() { let result = get_new_vector(); println!("{:?}", result); }
["a", "b"]
Equals. We can compare 2 vectors for equality by using the equality operator. This compares the actual elements, so we are testing logical equality.
Vec Equals
fn main() { // Create 2 vectors. let values1 = vec![1, 2, 3]; let mut values2 = Vec::new(); values2.push(1); values2.push(2); values2.push(3); // Test vectors for equality. if values1 == values2 { println!("EQUAL"); } }
EQUAL
Collect. Often in Rust we have an iterator and want to get a vector from it. The collect() function, with the TurboFish operator, is helpful here.
collect
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.
swap
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."
Vec Nested
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.
get unchecked
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).
Home
Changes
© 2007-2024 Sam Allen.