Skip to content

Vec and String

Vec<T> is Rust’s workhorse sequential collection. It stores elements of type T contiguously on the heap and automatically resizes when you push beyond its current capacity.

let mut v: Vec<i32> = Vec::new(); // empty Vec
let w = vec![1, 2, 3]; // vec! macro shorthand
let mut v = vec![10, 20, 30];
v.push(40); // append an element
let last = v.pop(); // remove and return the last element → Some(40)
let first = v[0]; // index — panics if out of bounds
let safe = v.get(100); // returns Option<&T> — None if out of bounds

Prefer .get(i) when the index might be out of range so you handle the None case instead of panicking.

let nums = vec![1, 2, 3];
for n in &nums { // borrow each element as &i32
println!("{}", n);
}
// nums is still valid after the loop because we only borrowed it

Using &nums borrows the vector; using nums would move it and make it unavailable after the loop.

String is Rust’s heap-allocated, mutable text type. It owns its bytes and manages deallocation.

let mut s = String::from("hello");
s.push(' '); // push a single char
s.push_str("world"); // append a string slice
let combined = s + " again"; // + moves s and appends
String&str
OwnershipOwnedBorrowed slice
LocationHeapStack / static / inside a String
MutableYes (with mut)No
Typical useBuilding/modifying textReading text

A &String automatically coerces to &str, so prefer &str in function parameters for maximum flexibility.

fn shout(s: &str) -> String {
s.to_uppercase()
}
let owned = String::from("hello");
shout(&owned); // &String coerces to &str
shout("world"); // string literal is already &str
fn main() {
// Vec<i32> basics
let mut nums: Vec<i32> = Vec::new();
nums.push(10);
nums.push(20);
nums.push(30);
println!("len={}, first={}", nums.len(), nums[0]);
// pop returns Option<T>
let last = nums.pop();
println!("popped: {:?}, remaining: {:?}", last, nums);
// safe get returns Option<&T>
println!("get(5) = {:?}", nums.get(5));
// iterate by reference — nums still valid after
for n in &nums {
println!("item: {}", n);
}
println!("nums still valid: {:?}", nums);
// String vs &str
let literal: &str = "hello";
let mut owned: String = String::from(literal);
owned.push_str(" world");
println!("{}", owned);
// slice into a String
let slice: &str = &owned[0..5];
println!("slice: {}", slice);
}
What does Vec::pop() return when the Vec is empty?
What is the difference between iterating with &nums versus nums?
Why should function parameters prefer &str over &String?
Which method should you use instead of v[i] when the index might be out of bounds?