| title | slug |
|---|---|
Vectors |
vectors |
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.
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![];let a: Vec<i32> = Vec::new();
let b: Vec<i32> = vec![];
let c = vec![1i32, 2, 3]; // Suffixing 1st value with data typelet 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 "" strlet 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.
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 5Similar 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.
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 = NoneIn 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.
-
💯 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̐éö̲\r\n"; for (i, v) in a.chars().enumerate() { println!("{i}: {v}"); } // 0: a // 1: ̐ // 2: é // 3: ö // 4: ̲ // 5: // 6: