Chapter 25: Rust Vectors
Rust Vectors (Vec<T>), which are the dynamic, growable version of arrays and one of the most used data structures in almost every real Rust program.
In Rust, when you need a list that can grow, shrink, push/pop elements at runtime — you almost always reach for Vec<T> (pronounced “vector”). It’s like Python’s list, Java’s ArrayList, or C++’s std::vector — but with Rust’s famous memory safety guarantees.
Let me explain Rust Vec like your personal teacher sitting next to you with code open: slowly, step by step, with lots of copy-paste examples you can run right now, Hyderabad-flavored analogies (traffic jam, biryani orders, phone contacts), comparisons, and all the important methods & patterns.
1. What is a Vec<T>? (The Core Idea)
Vec<T> = contiguous growable array stored on the heap.
- It can change size at runtime (grow with push, shrink with pop, remove, etc.)
- Elements are all the same type T
- Internally: pointer + length + capacity (heap-allocated buffer)
- Type: Vec<T> (no fixed size in the type — unlike [T; N])
Analogy: An array [T; 7] is like a fixed table with exactly 7 chairs — you can’t add more people. A Vec<T> is like a banquet hall in Hyderabad with expandable chairs — you start with some, but you can keep adding guests (push), remove some (pop), and the hall automatically gets bigger when needed.
2. Creating a Vec – All Common Ways
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
fn main() { // Way 1: Empty vector let mut empty: Vec<i32> = Vec::new(); // Way 2: vec! macro (most common & readable) let mut numbers = vec![1, 2, 3, 4, 5]; // Way 3: with initial capacity (performance hint) let mut large = Vec::with_capacity(100); // pre-allocates space for 100 elements // Way 4: from array or iterator let from_array = Vec::from([10, 20, 30]); let from_range = (1..=10).collect::<Vec<i32>>(); println!("Numbers: {:?}", numbers); } |
- mut required if you plan to change it (push/pop/etc.)
- vec![1, 2, 3] is shorthand for creating + initializing
3. Basic Operations – Push, Pop, Access
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
fn main() { let mut cities = vec!["Hyderabad", "Secunderabad"]; // Add elements cities.push("Warangal"); cities.push("Nizamabad"); println!("After push: {:?}", cities); // Remove last let last = cities.pop(); // returns Option<&str> println!("Popped: {:?}, now: {:?}", last, cities); // Access by index (panics if out of bounds) println!("First city: {}", cities[0]); // Safe access if let Some(city) = cities.get(10) { println!("City at 10: {}", city); } else { println!("No city at index 10"); } } |
- push — adds to end (amortized O(1))
- pop — removes from end, returns Option<T>
- Indexing [0] — fast, but panics on invalid index
- .get(index) — returns Option<&T> (safe)
4. Iterating & Borrowing with Vec
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
fn main() { let mut temperatures = vec![28.5, 30.0, 32.1, 35.0]; // Immutable borrow — read only for temp in &temperatures { println!("Temp: {}°C", temp); } // Mutable borrow — change elements for temp in &mut temperatures { *temp += 2.0; // dereference to mutate } println!("Warmer: {:?}", temperatures); // Into iterator — consumes the Vec (moves out) for temp in temperatures { println!("Consumed temp: {}", temp); } // temperatures is moved — can't use anymore } |
- &vec or &temperatures → immutable references (&T)
- &mut vec → mutable references (&mut T)
- vec.into_iter() → takes ownership, gives owned T
5. Common Methods You’ll Use Every Day
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
fn main() { let mut scores = vec![85, 92, 78, 95, 88]; // Length & capacity println!("Len: {}, Capacity: {}", scores.len(), scores.capacity()); // Insert / remove at position scores.insert(2, 100); // insert at index 2 scores.remove(1); // remove at index 1 // Clear // scores.clear(); // keep capacity, remove all // Sort scores.sort(); // ascending scores.sort_by(|a, b| b.cmp(a)); // descending // Find / contains if scores.contains(&95) { println!("95 found!"); } // Drain / truncate let drained: Vec<_> = scores.drain(1..3).collect(); // remove range, get iterator } |
- len() vs capacity() — len = how many elements, capacity = reserved space
- reserve(n) / with_capacity(n) — avoid reallocations
- sort(), sort_unstable(), sort_by_key()
6. Vec vs Array vs Slice – Quick Recap
| Feature | Vec<T> | Array [T; N] | Slice &[T] |
|---|---|---|---|
| Size | Dynamic | Fixed compile-time | View (dynamic length) |
| Memory | Heap | Stack | Borrowed |
| Grow/Shrink | Yes | No | No |
| Type | Vec<i32> | [i32; 5] | &[i32] |
| Best for | Most real lists | Small fixed data | Function params, views |
7. Real-World Example – Managing a Shopping List
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
fn main() { let mut shopping_list = Vec::new(); // Add items shopping_list.push("Rice 5kg"); shopping_list.push("Mirchi 500g"); shopping_list.push("Biryani masala"); shopping_list.push("Onions"); println!("Shopping list ({} items):", shopping_list.len()); // Print with numbers for (i, item) in shopping_list.iter().enumerate() { println!("{}. {}", i + 1, item); } // Remove if already bought if let Some(pos) = shopping_list.iter().position(|&x| x == "Onions") { shopping_list.remove(pos); println!("Onions bought — removed!"); } println!("Updated list: {:?}", shopping_list); } |
Teacher Summary & Tips
Rust Vec<T> =
- Your go-to dynamic array — growable, resizable, heap-allocated
- Created with Vec::new(), vec![…], collect()
- Push/pop fast at end, insert/remove slower in middle
- Borrow with & / &mut — same rules as before
- Prefer &[T] in function parameters (accepts &Vec<T>, &[T], array slices)
- Use .reserve() when you know approximate size — big performance win
Practice challenge:
- Create Vec<String> of favorite foods → sort alphabetically
- Make Vec<i32> of 1..=20 → remove even numbers
- Function that takes &[i32] and returns sum + average
- Try push 1000 elements → check len vs capacity
Next ready?
- Slices (&[T]) in much more detail?
- Iterators on Vec (.map, .filter, .collect, .fold…)?
- Common patterns with Vec (split, join, dedup, etc.)?
- Or structs containing Vec + ownership/borrowing?
Just tell me — your Rust class is going strong! 🦀🚀
