Skip to content
DocsRust LearningintermediateCollections
Chapter 9 of 19·intermediate·7 min read

Collections

Bộ Sưu Tập

Vec, String, and HashMap

Hover or tap any paragraph to see Vietnamese translation

Vec<T>: Creating, Updating, Reading

Vec<T> is Rust's most common dynamic array type. It stores multiple elements of the same type contiguously on the heap. When a vector needs more space it reallocates automatically.

vec_basics.rs
1fn main() {2    // Create an empty vector and push values3    let mut v: Vec<i32> = Vec::new();4    v.push(1);5    v.push(2);6    v.push(3);78    // Create with initial values using the vec! macro9    let v2 = vec![10, 20, 30, 40, 50];1011    // Reading elements12    let third = &v2[2];          // panics if out of bounds
Tip
Use .get(i) instead of v[i] when the index might be out of bounds — it returns Option<&T> instead of panicking.

Vector Iteration and Ownership

When iterating over a vector you must decide whether you want immutable references, mutable references, or to consume the vector by taking ownership. Each form has different semantics.

vec_iteration.rs
1fn main() {2    let v = vec![1, 2, 3, 4, 5];34    // Immutable iteration — borrows vector5    for x in &v {6        print!("{x} "); // x is &i327    }8    println!();9    println!("v still usable: {:?}", v);1011    // Mutable iteration — mutably borrows12    let mut v2 = vec![1, 2, 3];

String Type in Depth (UTF-8)

A Rust String is an owned, growable UTF-8 byte sequence. Not every byte sequence is valid UTF-8, so Rust separates String (valid UTF-8) from Vec<u8> (raw bytes). Any indexing must be done carefully because characters can be multiple bytes wide.

string_utf8.rs
1fn main() {2    // Creating strings3    let s1 = String::new();4    let s2 = String::from("hello");5    let s3 = "world".to_string();6    let s4 = format!("{s2} {s3}");7    println!("{s4}"); // hello world89    // UTF-8: each char can be 1–4 bytes10    let emoji = String::from("Hello");11    println!("bytes: {}", emoji.len());         // byte length, not char count12    println!("chars: {}", emoji.chars().count()); // actual character count
Warning
String cannot be integer-indexed because one Unicode character may occupy multiple bytes. Rust prioritizes correctness over convenience here.

String Operations and Slicing

Rust provides many methods for string manipulation. String slicing (&str) works on byte boundaries, so always ensure your indices land on valid character boundaries.

string_ops.rs
1fn main() {2    let mut s = String::from("hello");34    // Concatenation5    s.push(' ');6    s.push_str("world");7    println!("{s}"); // hello world89    // + operator moves left-hand side10    let s1 = String::from("Hello, ");11    let s2 = String::from("world!");12    let s3 = s1 + &s2; // s1 is moved here, s2 is borrowed

HashMap<K, V>: Creating and Updating

HashMap stores key-value pairs and allows O(1) average lookup by key. Keys must implement the Eq and Hash traits.

hashmap_basics.rs
1use std::collections::HashMap;23fn main() {4    // Create and populate5    let mut scores: HashMap<String, i32> = HashMap::new();6    scores.insert(String::from("Alice"), 100);7    scores.insert(String::from("Bob"), 85);8    scores.insert(String::from("Carol"), 92);910    // Build from iterators11    let names = vec!["Alice", "Bob"];12    let points = vec![100, 85];

Entry API for HashMap

The Entry API is the most idiomatic way to update a HashMap because it avoids looking up the key twice. It returns an Entry enum that lets you manipulate the value depending on whether the key exists.

entry_api.rs
1use std::collections::HashMap;23fn main() {4    let mut scores: HashMap<String, i32> = HashMap::new();56    // Insert only if key is absent7    scores.entry(String::from("Yellow")).or_insert(50);8    scores.entry(String::from("Yellow")).or_insert(200); // no effect9    println!("{scores:?}"); // {"Yellow": 50}1011    // Insert with a default computed value12    scores.entry(String::from("Blue")).or_insert_with(|| 42 * 2);

HashSet<T> Basics

HashSet is a HashMap with no values — it stores only unique keys. It is useful for membership testing and deduplication. HashSet supports standard set operations: union, intersection, difference.

hashset.rs
1use std::collections::HashSet;23fn main() {4    let mut set: HashSet<i32> = HashSet::new();5    set.insert(1);6    set.insert(2);7    set.insert(3);8    set.insert(2); // duplicate, ignored9    println!("{set:?}"); // {1, 2, 3} (order not guaranteed)1011    // Membership12    println!("{}", set.contains(&2)); // true

When to Use Which Collection

Choosing the right collection significantly affects both performance and code readability. Here is practical guidance.

Quick Summary

  • Vec<T> — ordered list, index access, frequent appending to the end
  • VecDeque<T> — double-ended queue, efficient push/pop at both ends
  • LinkedList<T> — rarely needed; Vec is usually faster due to cache locality
  • HashMap<K, V> — fast key-value lookup, order does not matter
  • BTreeMap<K, V> — sorted key-value pairs, useful when iteration order matters
  • HashSet<T> — fast O(1) membership testing, no duplicates
  • BTreeSet<T> — sorted set, useful for ranges
  • String — owned UTF-8 text, prefer over Vec<u8> for text
Tip
When in doubt, start with Vec and HashMap. They cover the vast majority of use cases and perform well in practice.

Key Takeaways

Điểm Chính

  • Vec<T> is a growable array stored on the heapVec<T> là mảng có thể mở rộng lưu trên heap
  • String is a UTF-8 encoded, growable text typeString là kiểu văn bản mã hóa UTF-8 có thể mở rộng
  • HashMap<K, V> stores key-value pairs with O(1) average lookupHashMap<K, V> lưu cặp khóa-giá trị với tra cứu trung bình O(1)
  • Use .get() for safe access that returns Option instead of panickingDùng .get() để truy cập an toàn trả về Option thay vì panic

Practice

Test your understanding of this chapter

Quiz

Which Vec method safely retrieves an element without panicking if the index is out of bounds?

Phương thức Vec nào truy xuất phần tử một cách an toàn mà không panic nếu chỉ số vượt giới hạn?

Quiz

What traits must HashMap keys implement?

Các khóa của HashMap phải triển khai những trait nào?

True or False

A Rust String can be indexed by an integer to access individual characters, just like in Python or JavaScript.

Một String trong Rust có thể được lập chỉ mục bằng số nguyên để truy cập từng ký tự, giống như trong Python hay JavaScript.

True or False

Inserting a duplicate value into a HashSet is allowed and simply has no effect — the set remains unchanged.

Chèn một giá trị trùng lặp vào HashSet được phép và đơn giản là không có tác dụng — tập hợp vẫn không thay đổi.

Code Challenge

Count word frequencies using the Entry API

Đếm tần suất từ bằng Entry API

let mut word_count: HashMap<&str, i32> = HashMap::new();
for word in text.split_whitespace() {
    let count = word_count.entry(word).(0);
    *count += 1;
}

Chapter Complete!

Great job! Keep the momentum going.

Your progress0 of 19 chapters read
← → to navigate chapters
Built: 4/8/2026, 12:01:11 PM