Collections
An Introduction to Rust's Standard Collections
Rust's standard library provides a rich set of data structures for managing data, all designed with the language's core principles of performance and safety in mind. These collections are generic, meaning they can hold any type of data, and they're highly optimized.
Vec<T>
: The Growable Array
The Vec<T>
is a dynamic, growable array, a lot like std::vector
in C++. It's one of the most widely used collections in Rust. It stores values on the heap, so its size can change at runtime.
Key Features:
-
Contiguous Storage: Elements are stored next to each other in memory, making access very fast.
-
Heap-allocated: The data is stored on the heap, and the
Vec
itself is a small pointer on the stack, which points to the data. -
Growable: You can add and remove elements.
push()
will handle reallocation if needed.
Example:
// Create a new, empty Vec that will hold i32 values.
let mut numbers: Vec<i32> = Vec::new();
// Add elements to the Vec.
numbers.push(10);
numbers.push(20);
numbers.push(30);
// You can also create a Vec with initial values using a macro.
let mut other_numbers = vec![1, 2, 3];
// Accessing elements by index. This will panic if the index is out of bounds.
let third = numbers[2];
println!("The third element is: {}", third);
// A safer way to get an element is with the `.get()` method, which returns an Option.
match numbers.get(10) {
Some(value) => println!("The value at index 10 is: {}", value),
None => println!("Index 10 is out of bounds."),
}
String
& &str
: The Text Types
Working with text in Rust involves two main types, which can be a point of confusion for beginners.
-
String
: This is a heap-allocated, growable, and mutable UTF-8 encoded string. It's the equivalent ofstd::string
in C++. You use it when you need to own the data and potentially modify it. -
&str
(string slice): This is an immutable view or "slice" into aString
or a string literal. It's a pointer to the data and its length. You can think of it likeconst char*
in C++, but safer and aware of its length.&str
is often used for function arguments.
Example:
// A mutable, heap-allocated String.
let mut s1 = String::from("hello");
// A string slice, which is a view into the string literal.
let s2: &str = "world";
// You can push characters or strings onto a String.
s1.push_str(", ");
s1.push_str(s2);
s1.push('!');
println!("{}", s1); // Prints "hello, world!"
// String slices can be created from Strings.
let slice = &s1[0..5]; // This creates a slice "hello".
println!("A slice: {}", slice);
// Note that you cannot use an index to access a character directly,
// as a single character might take up more than one byte in UTF-8.
HashMap<K, V>
: The Key-Value Store
HashMap<K, V>
is a collection that stores data as key-value pairs, similar to std::unordered_map
in C++. It provides an efficient way to look up a value based on its key.
Key Features:
-
Key-Value Pairs: Stores data in a
(key, value)
format. -
Efficient Lookups: Provides average time complexity for insertion and retrieval.
-
Hashing: The key type
K
must implement theHash
trait so theHashMap
can determine where to store the data. It also needs to implement theEq
trait for comparison.
Example:
use std::collections::HashMap;
// Create a new HashMap. The compiler will infer the types.
let mut scores = HashMap::new();
// Insert key-value pairs.
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// Get a value from the HashMap. This returns an Option<&V>.
let team_name = String::from("Blue");
let score = scores.get(&team_name);
match score {
Some(value) => println!("The blue team's score is: {}", value),
None => println!("No score found for that team."),
}
// Iterate over the HashMap's key-value pairs.
for (key, value) in &scores {
println!("{}: {}", key, value);
}
Other Useful Collections
Beyond the core collections, Rust's standard library offers several others for more specific use cases. Here's a brief mention of them with links to their official documentation for further exploration.
-
HashSet<T>
: A collection of unique values, similar tostd::unordered_set
in C++. It offers fast, average time complexity for insertion, deletion, and checking for the presence of an item. -
BTreeMap<K, V>
: A map that stores key-value pairs in a sorted order, comparable tostd::map
in C++. It has a logarithmic time complexity for operations but guarantees a consistent, sorted iteration order. -
BTreeSet<T>
: A collection of unique, sorted values, similar tostd::set
in C++. It also provides logarithmic time complexity and guarantees sorted iteration. -
VecDeque<T>
: A double-ended queue, which is a growable array optimized for efficient pushes and pops from both the front and the back. -
LinkedList<T>
: A classic doubly-linked list. While aVecDeque
is often a better choice, aLinkedList
can be more efficient for frequent insertions and deletions at arbitrary positions.