Vectors
Arrays' strength is also their weakness: their size must be known upfront, at compile-time. If you try to create an array with a size that's only known at runtime, you'll get a compilation error:
#![allow(unused)] fn main() { let n = 10; let numbers: [u32; n]; }
error[E0435]: attempt to use a non-constant value in a constant
--> src/main.rs:3:20
|
2 | let n = 10;
3 | let numbers: [u32; n];
| ^ non-constant value
Arrays wouldn't work for our ticket management system—we don't know how many tickets we'll need to store at compile-time.
This is where Vec
comes in.
Vec
Vec
is a growable array type, provided by the standard library.
You can create an empty array using the Vec::new
function:
#![allow(unused)] fn main() { let mut numbers: Vec<u32> = Vec::new(); }
You would then push elements into the vector using the push
method:
#![allow(unused)] fn main() { numbers.push(1); numbers.push(2); numbers.push(3); }
New values are added to the end of the vector.
You can also create an initialized vector using the vec!
macro, if you know the values at creation time:
#![allow(unused)] fn main() { let numbers = vec![1, 2, 3]; }
Accessing elements
The syntax for accessing elements is the same as with arrays:
#![allow(unused)] fn main() { let numbers = vec![1, 2, 3]; let first = numbers[0]; let second = numbers[1]; let third = numbers[2]; }
The index must be of type usize
.
You can also use the get
method, which returns an Option<&T>
:
#![allow(unused)] fn main() { let numbers = vec![1, 2, 3]; assert_eq!(numbers.get(0), Some(&1)); // You get a `None` if you try to access an out-of-bounds index // rather than a panic. assert_eq!(numbers.get(3), None); }
Access is bounds-checked, just like element access with arrays. It has O(1) complexity.
Memory layout
Vec
is a heap-allocated data structure.
When you create a Vec
, it allocates memory on the heap to store the elements.
If you run the following code:
#![allow(unused)] fn main() { let mut numbers = Vec::with_capacity(3); numbers.push(1); numbers.push(2); }
you'll get the following memory layout:
+---------+--------+----------+
Stack | pointer | length | capacity |
| | | 2 | 3 |
+--|------+--------+----------+
|
|
v
+---+---+---+
Heap: | 1 | 2 | ? |
+---+---+---+
Vec
keeps track of three things:
- The pointer to the heap region you reserved.
- The length of the vector, i.e. how many elements are in the vector.
- The capacity of the vector, i.e. the number of elements that can fit in the space reserved on the heap.
This layout should look familiar: it's exactly the same as String
!
That's not a coincidence: String
is defined as a vector of bytes, Vec<u8>
, under the hood:
#![allow(unused)] fn main() { pub struct String { vec: Vec<u8>, } }
Exercise
The exercise for this section is located in 06_ticket_management/02_vec