If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is kind of a re-sizable array but all elements must be in the same type.
π‘
Vec<T>: capital βVβ as itβs a struct.
Itβs a generic type, written as Vec<T>. T can have any type, ex. A vector of i32s is Vec<i32>. Also, Vectors always allocate their data in a dynamically allocated heap.
Creation
Empty Vector
let mut a = Vec::new(); // 1. With new() keyword
let mut b = vec![]; // 2. Using the vec! macro (π‘ usually create with values same time)
// βοΈ If you need an immutable empty vector, you must have to specify the data type.
let a: Vec<i32> = Vec::new();
let b: Vec<String> = vec![];
With Type Annotations
let a: Vec<i32> = Vec::new();
let b: Vec<i32> = vec![];
let c = vec![1i32, 2, 3]; // Suffixing 1st value with data type
With Values
let a = vec![1, 2, 3];
let b: Vec<i32> = vec![1, 2, 3];
let c = vec![1i32, 2, 3];
let a = vec![0; 10]; // Ten zeroes
let b = vec![""; 10]; // Ten "" str
With a Capacity
let mut a: Vec<i32> = Vec::with_capacity(10);
println!("Length: {}, Capacity : {}", a.len(), a.capacity()); // Length: 0, Capacity : 10
π We’ll discuss this in the Length and Capacity.
Accessing and Changing Elements
By Index
We can access and change elements of a vector, via the index (like we access/ change elements of an array).
let mut a = vec![1, 2, 3];
println!("{} {} {}", a[0], a[1], a[2]); // 1 2 3
a[0] = 4; // [4, 2, 3]
(a[1], a[2]) = (a[2], a[1]); // Shuffle
println!("{:?}", a); // [4, 3, 2]
a[5] = 2; // π₯ panics at runtime; index out of bounds: the len is 3 but the index is 5
By the get Method
Similar to accessing elements via the index, but safer, as it always returns an Option<T>/ optional value.
let mut a = vec![1, 2, 3];
let x = a.get(0); // Some(1)
let y = a.get(5); // None
π Option<T> can be either Some value or None (no value). It is also a generic type, like Vec<T>. Weβll discuss more details in the Generics: Option. For the moment, focus on vectors.
By the push and pop Methods
let mut a: Vec<i32> = Vec::new();
a.push(1); // Add 1 to the end; a = [1]
a.push(2); // Add 2 to the end; a = [1, 2]
a.pop(); // Remove 2 from the end; a = [1]
let x = a.pop(); // Remove 1 from the end and assign it to x as Option<T>; a = []
// x = Some(1)
let y = a.pop(); // Remove nothing as a is empty; a = [] βοΈ No panics
// y = None
Length and Capacity
In Rust, most types have a fixed size known at compile time and implement the trait Sized. Vec<T> is also a sized type; A struct that internally stores,
- A pointer: points to the heap-allocated memory storing the elements contiguously, like a slice
[T] - Length: NO of elements currently have
- Capacity: Amount of space allocated for any future elements
βοΈ If the length of a vector exceeds its capacity, its capacity will be increased automatically. But its elements will be reallocated(which can be slow). So, always use Vec::with_capacity whenever itβs possible.
let mut e: Vec<i32> = Vec::with_capacity(10); // Length: 0, Capacity : 10
// These are all done without reallocating...
for i in 0..10 {
e.push(i);
}
// ...but this may make the vector reallocate as exceeded current capacity
e.push(11);
π Dynamically sized types (DSTs)/ unsized types donβt have a fixed size at compile time, and the size is known only at run-time. Slices and trait objects are two examples of DSTs.
π¨βπ« Before going to the next…
π― Vectors can be used with iterators in three ways,
let mut a = vec![1, 2, 3, 4, 5]; for i in a { println!("Take ownership of the vector and its element {}", i); } for i in &a { println!("A reference to {}", i); } for i in &mut a { println!("A mutable reference to {}", i); }π― The
String/&strdata types are UTF-8 encoded vectors. But you can not index into a String because of encoding.βοΈ We can iterate over the characters of a string via the
chars()method. But for more accurate results, you should use a crate likeunicode_segmentationthat follows more accurate Unicode text segmentation standards.let a = String::from("Hello!"); print!("{}", a.chars().count()); // 6 // π‘ H e l l o !let a = "aΜeΜoΜΜ²\r\n"; for (i, v) in a.chars().enumerate() { println!("{i}: {v}"); } // 0: a // 1: Μ // 2: Γ© // 3: ΓΆ // 4: Μ² // 5: // 6: