Vec<T> is a growable list, storing its elements on the heap; String is a growable, heap-allocated, UTF-8-encoded piece of text (distinct from &str, a borrowed string slice); HashMap<K, V> stores key-value pairs with average O(1) lookup. All three grow at runtime, unlike Rust's fixed-size arrays.
Vec<T>: a growable list
Unlike an array, a Vec's length isn't part of its type — it can grow and shrink at runtime, backed by a reallocating heap buffer similar in spirit to Go's slices.
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
v.push(3);
let v2 = vec![1, 2, 3]; // macro shorthand, equivalent to the above
println!("{}", v2[0]); // 1 — panics if out of bounds
println!("{:?}", v2.get(10)); // None — safe alternative, no panicString vs. &str
String owns its data and can grow; &str is a borrowed view (a slice) into string data owned by something else — often a string literal or a String. Function parameters conventionally take &str so they accept both a literal and a borrowed String.
let mut s = String::from("hello");
s.push_str(", world"); // String can grow
s.push('!');
fn greet(name: &str) { // accepts &str: works with literals AND &String
println!("Hello, {}!", name);
}
greet("Bob"); // a string literal, is &str
greet(&s); // a borrowed String, coerces to &strHashMap<K, V>: key-value pairs
entry().or_insert() is the idiomatic way to insert a default only if the key is missing, in a single call — a common pattern for counting or building up grouped data.
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
match scores.get("Blue") {
Some(score) => println!("{}", score), // 10
None => println!("no such team"),
}
// count word occurrences: entry().or_insert() pattern
let text = "the quick fox the lazy fox";
let mut counts: HashMap<&str, i32> = HashMap::new();
for word in text.split_whitespace() {
*counts.entry(word).or_insert(0) += 1;
}
println!("{:?}", counts.get("fox")); // Some(2)