In Rust, memory management has traditionally been considered one of the most complex issues in low-level programming. Before Rust, many languages required manual memory management (like C/C++), leading to leaks and data corruption. Rust approached the problem differently — collections like Vec<T> use an automatic and safe memory management strategy, controlling the moments of allocation, reallocation (resizing), and memory release through a system of ownership and borrowing.
The problem was that most languages either abstract the details of the allocator (GC) too much or make the programmer responsible for everything (malloc/free). In the case of dynamic arrays, it is crucial to watch for leaks and out-of-bounds access, as well as not violate ownership.
The solution in Rust is automation through safe abstractions. Vec<T> allocates memory on the heap, increases its size dynamically (usually with exponential growth), and frees everything upon exiting the scope (RAII).
Example code:
fn main() { let mut v: Vec<i32> = Vec::new(); v.push(1); v.push(2); v.push(3); // Adding elements may trigger size increase and memory reallocation println!("Vector: {:?}", v); // Memory is freed automatically when exiting main }
Key features:
Vec<T> allocates memory in advance and reallocates it as necessaryWhat is the complexity of growing the array when adding elements to Vec?
The usual complexity of push is amortized O(1), however, when the array overflows, a new memory block is allocated (approximately doubling the size), and all elements are copied. This moment is the only exception where the operation becomes O(n).
What will happen if you try to access an out-of-bounds element using v[index]?
Using square brackets will cause a panic when going out of bounds. You should use the .get() method, which returns Option and allows you to handle the error safely.
let element = v.get(10); // None if the index is out of bounds
Can you use a reference to an element of Vec after potential growth (resize) of the vector?
No, after changing the size of the vector (for example, through push when overflowing), all memory may be moved, and old references become invalid — a compilation error occurs (or undefined behavior in an unsafe block if you use them manually).
A developer implements a message cache based on Vec<T> and exposes references to elements. After a new insertion, memory reallocation occurs, making all existing references "dangling." The application crashes.
Pros:
Cons:
Either internal identification of elements is used (indices/keys + validity checks), or only copies/immutable values are returned, and long-lived references to Vec elements are not allowed.
Pros:
Cons: