Vec<T> — Dynamic Arrays
Vec<T> is Rust's growable, heap-allocated array. It's the most commonly
used collection, similar to ArrayList in Java or std::vector in C++.
fn main() {
// Creating vectors
let mut v: Vec = Vec::new();
let v2 = vec![1, 2, 3, 4, 5]; // vec! macro
// Adding elements
v.push(1);
v.push(2);
v.push(3);
// Accessing elements
let third = &v[2]; // Panics if out of bounds
let maybe = v.get(2); // Returns Option<&T>
println!("Third: {third}, Maybe: {:?}", maybe);
// Iterating
for val in &v {
print!("{val} ");
}
println!();
// Mutable iteration
for val in &mut v {
*val *= 2;
}
println!("{:?}", v); // [2, 4, 6]
// Useful methods
let mut nums = vec![3, 1, 4, 1, 5, 9, 2, 6];
nums.sort();
nums.dedup(); // Remove consecutive duplicates
println!("Sorted unique: {:?}", nums);
println!("Contains 5? {}", nums.contains(&5));
println!("Length: {}, Capacity: {}", nums.len(), nums.capacity());
// Removing elements
let last = nums.pop(); // Remove last
let second = nums.remove(1); // Remove at index (shifts elements)
nums.retain(|&x| x > 3); // Keep only elements > 3
println!("After removes: {:?}", nums);
// Slices
let slice = &v2[1..3]; // [2, 3]
println!("Slice: {:?}", slice);
}
String vs &str
String Types in Rust
- String: Owned, heap-allocated, growable UTF-8 string. Use when you need to own or modify the string.
- &str: Borrowed string slice. A view into a String or string literal. Use in function parameters.
fn main() {
// String (owned)
let mut s = String::from("Hello");
s.push_str(", world");
s.push('!');
println!("{s}");
// &str (borrowed)
let greeting: &str = "Hello"; // String literal is &'static str
let slice: &str = &s[0..5]; // Slice of a String
// Conversions
let owned: String = greeting.to_string();
let also_owned: String = String::from("hello");
let borrowed: &str = &owned;
// String concatenation
let hello = String::from("Hello");
let world = String::from(" World");
let combined = hello + &world; // hello is moved!
// Use format! to avoid moves:
let a = String::from("tic");
let b = String::from("tac");
let c = String::from("toe");
let result = format!("{a}-{b}-{c}"); // No moves!
println!("{result}");
// Iterating over characters (Rust strings are UTF-8)
for c in "hello".chars() {
print!("{c} ");
}
println!();
// String methods
let s = String::from(" Hello, Rust! ");
println!("Trimmed: '{}'", s.trim());
println!("Uppercase: {}", s.to_uppercase());
println!("Contains Rust? {}", s.contains("Rust"));
println!("Replace: {}", s.replace("Rust", "World"));
let parts: Vec<&str> = "a,b,c".split(',').collect();
println!("Split: {:?}", parts);
}
HashMap<K, V>
use std::collections::HashMap;
fn main() {
let mut scores: HashMap = HashMap::new();
// Insert
scores.insert(String::from("Alice"), 100);
scores.insert(String::from("Bob"), 85);
scores.insert(String::from("Charlie"), 92);
// Access
let alice_score = scores.get("Alice"); // Option<&i32>
println!("Alice: {:?}", alice_score);
// Entry API — insert only if key doesn't exist
scores.entry(String::from("Alice")).or_insert(0); // Already exists
scores.entry(String::from("Dave")).or_insert(78); // Inserted
// Entry API — update based on old value
let text = "hello world hello rust hello";
let mut word_count: HashMap<&str, i32> = HashMap::new();
for word in text.split_whitespace() {
let count = word_count.entry(word).or_insert(0);
*count += 1;
}
println!("Word counts: {:?}", word_count);
// Iterating
for (name, score) in &scores {
println!("{name}: {score}");
}
// Useful methods
println!("Contains Alice? {}", scores.contains_key("Alice"));
println!("Length: {}", scores.len());
scores.remove("Bob");
// Collect into HashMap
let teams = vec!["Red", "Blue", "Green"];
let initial_scores = vec![0, 0, 0];
let team_scores: HashMap<_, _> = teams
.into_iter()
.zip(initial_scores.into_iter())
.collect();
println!("{:?}", team_scores);
}
HashSet, BTreeMap, and VecDeque
use std::collections::{HashSet, BTreeMap, VecDeque};
fn main() {
// HashSet — unique values, fast lookup
let mut fruits: HashSet<&str> = HashSet::new();
fruits.insert("apple");
fruits.insert("banana");
fruits.insert("apple"); // Duplicate ignored
println!("Fruits: {:?}", fruits); // {"apple", "banana"}
let set_a: HashSet = [1, 2, 3, 4].into();
let set_b: HashSet = [3, 4, 5, 6].into();
println!("Union: {:?}", &set_a | &set_b);
println!("Intersection: {:?}", &set_a & &set_b);
println!("Difference: {:?}", &set_a - &set_b);
// BTreeMap — sorted keys, O(log n) operations
let mut btree = BTreeMap::new();
btree.insert("charlie", 3);
btree.insert("alice", 1);
btree.insert("bob", 2);
// Always iterates in key order:
for (k, v) in &btree {
println!("{k}: {v}"); // alice, bob, charlie
}
// VecDeque — double-ended queue
let mut deque: VecDeque = VecDeque::new();
deque.push_back(1);
deque.push_back(2);
deque.push_front(0);
println!("Deque: {:?}", deque); // [0, 1, 2]
println!("Pop front: {:?}", deque.pop_front()); // Some(0)
println!("Pop back: {:?}", deque.pop_back()); // Some(2)
}
Key Takeaways
- ✅
Vec<T>is the go-to growable array — use it for ordered collections - ✅
Stringis owned and growable;&stris a borrowed view — prefer&strin function parameters - ✅
HashMap's entry API elegantly handles insert-or-update patterns - ✅
HashSetfor unique values,BTreeMapfor sorted keys,VecDequefor double-ended access - ✅ All collections are generic and integrate with Rust's ownership and borrowing system