Skip to main content

Collections in Rust - Vectors, Strings, Hashmaps

We've already dealt with several collections. Rust's standard library provides these data structures and aid in dynamic data manipulation and representation. Similar to arrays and tuples, collections can store more than value. In contrast to such compound types, collections point to items stored on the heap. The benefit of it being managed on the heap allows collections to be growable and generally more modifiable in runtime. One collection you have already encountered numerous times throughout this course is String, which represents a list of characters.

There are three commonly used collections in Rust:

  • Vectors - re-sizable arrays. Like slices, their size is not known at compile time, but they can grow or shrink at any time.
  • Hashmaps - key-value data structures that store a map of information.
  • Strings - a collection of characters that are stored on the heap.

Vectors

The official type declaration of a vector is Vec<T>, which represents a list of any generic type T. We'll go over generics in the next module, but for now, know it is used exactly how T is used in the Option<T> and Result<T, E> enums. Like arrays, Vectors can only store values of the same type.

To create a new, empty vector, we call the associated method new():

let vector: Vec<i32> = Vec::new();
// Optionally, one may also use the `vec!` macro with initialized values. Type is inferred.
let vector = vec![1, 2, 3];

Now that the vector is defined, it exposes several methods which we can use to manipulate it:

// Initialize a new vector
let vector: Vec<i32> = Vec::new();

// Push new items to it
vector.push(2); // [2]
vector.push(3); // [2, 3]
vector.push(4); // [2, 3, 4]

// Reading via .get()
let four: Option<i32> = vector.get(2) // retrieves an Option, which can be pattern matched

// Reading a vector via indexing - same as an array
// use with caution, the index may not exist!
let four: i32 = &vector[2];

A vector follows all rules of the borrow checker - meaning if the vector is mutable, then an immutable reference to an element is impossible. Vectors also aren't limited to scalar types - you may define a struct, and store a list of structs within a vector if desired.

Strings

The String collection should be very familiar by now. String is a collection of characters stored on the heap. The &str type, or string slice/literal is different from this type, as it's a primitive type, whereas String is a data structure part of the Rust standard library. At its core, a String is a wrapped Vec of bytes.

Strings are rather complicated yet advantageous data structures. If you want to learn more about Strings at a more technical level, please read the Rust book, as this is just meant to introduce how to use String.

To create a new String, use the associated function new(), along with others:

// An empty string
let s = String::new();

// Push a string literal to a String
s.push_str("Web");

// Push a single char using .push()
s.push('3');

Hashmaps

Hashmaps store a mapping of K to V, and in other languages, are often to referred to as a map, dictionary, or hash table. As with T in Vec<T>, K and V are generic types used to refer to any type. As with vectors, values within the hashmap are stored on the heap. If an external variable is placed as part of a hashmap insertion, then the hashmap owns that variable.

To create a new hashmap, the associated method new() is also used:

let mut balances = Hashmap::new();

To add new values to the balances map. This particular mapping maps a String to an i32:

balances.insert(String::from("Bader"), 100);

To access a value, a .get(key: K) method is also exposed, upon which the value may be fetched by a reference to the key (so as to prevent ownership):

let mut balances = Hashmap::new();
balances.insert(String::from("Bader"), 100);
let key = String::from("Bader");
let bader_bal: Option<i32> = balances.get(&key);

The Option returned must be adequately handled via pattern matching, or a default value must be provided to bader_bal.

Try it yourself!

What's happening here?

All three collections and their common, respective operations are shown above. For more info, be sure to checkout the Rust Book as well as the Rust Standard Library's documentation for each of these data structures.