Skip to main content
In this lesson, we explore Hashmaps in Rust. A Hashmap is a collection of key-value pairs where each key is unique. It provides fast lookups, insertions, and deletions by employing a hashing algorithm that maps keys to specific memory locations. For example, in a student record system, a student ID can be the key, while the student’s name or grade becomes the value. You can create a Hashmap using the HashMap::new() function or by collecting key-value pairs from an iterator. Below is an example demonstrating how to create an empty Hashmap and insert data into it:
In this example, an empty Hashmap is created to hold student names as keys and their grades as values. Data is inserted using the insert() method, and the complete map is printed to the console.

Creating a HashMap from an Iterator

You can also build a HashMap directly from an iterator. In the example below, we define two vectors—one for student names and one for their respective grades. The vectors are converted into iterators using the into_iter() method, paired using zip(), and finally collected into a Hashmap with collect().
Output:
Here, the keys “Alice”, “Bob”, and “Charlie” are associated with the grades 85, 78, and 92, respectively.

Inserting and Updating Elements

Elements can be added to a HashMap using the insert() method. If the key already exists, the new value will overwrite the existing one. Consider the following example that updates Alice’s grade:
Output:
Alternatively, you can use the entry() method, which inserts a value only if the key is not already present:
Output:

Accessing Values

There are two common methods to access values in a HashMap: using the get() method or indexing.

Using the get() Method

The get() method returns an Option (either Some(value) if the key exists or None if it does not). This allows you to handle both cases using pattern matching:
Output:

Using Indexing

Indexing provides a more concise way to access values, but it will panic if the key does not exist. Ensure that the key is present before using this method:
Output:

Updating Values with Ownership Considerations

The image is a slide titled "Updating Values in a HashMap," explaining two methods: overwriting with insert() and using entry() to insert or modify if the key doesn’t exist.

Overwriting with insert()

When you insert a key that already exists, its associated value is replaced by the new one:
Output:

Using entry()

The entry() method checks if the key exists. It inserts the provided value only if the key is missing; otherwise, the existing value remains unchanged:
Output:

Removing Elements and Iterating

To remove an element from a HashMap, use the remove() method with the key as its argument:
Output:
You can also iterate over a HashMap using a foreach loop to access both keys and values, which is useful for processing or displaying all elements in the map.

Common HashMap Methods

Several methods are frequently used with Hashmaps:
  • len() – Returns the number of elements.
  • is_empty() – Checks if the map is empty.
  • remove() – Removes a key-value pair.
  • contains_key() – Checks if a specific key exists.
For example, to check the length of a HashMap:
Output:
To check if a HashMap is empty:
Output:
And to check for a specific key:
Output:

Ownership and Borrowing with HashMaps

Moving Ownership

HashMaps in Rust do not implement the Copy trait, so moving a HashMap transfers ownership. In the following example, ownership of student_grades is transferred to new_student_grades, making the original variable inaccessible:
Attempting to use student_grades after this move results in a compile-time error.

Borrowing an Immutable Reference

You can borrow an immutable reference to a HashMap, allowing data access without transferring ownership. Both the original variable and its borrowed reference can be used concurrently:
Output:
While borrowing immutably, the HashMap cannot be modified. Any attempt to mutate it through an immutable reference will trigger a compile-time error.

Borrowing a Mutable Reference

To modify a HashMap while referencing it, borrow a mutable reference. The following example demonstrates how to insert a new element through a mutable reference:
Output:
Using a mutable reference allows modifications to the HashMap, and both the original and borrowed references reflect these changes.

Performance Considerations

Hashmaps are generally efficient, but several factors can affect their performance:
  • Hash Collisions: Multiple keys may hash to the same value, causing collisions. Rust uses a collision resolution strategy to manage these scenarios.
  • Rehashing: When a HashMap becomes overly full, it may rehash its entries into a larger array. Although rehashing is computationally expensive, Rust handles it automatically.
  • Hash Function: Rust’s default HashMap employs a cryptographically secure hashing algorithm. While this enhances security, it might be slower compared to non-secure alternatives. In performance-critical applications where security is less of a concern, you can opt for a different hashing function.
The image outlines performance considerations for HashMaps, focusing on hash collisions, load factor, and hashing functions, with specific notes on Rust's implementation.
Understanding rehashing, load factors, and the underlying hash functions can help optimize the performance of your application when working with Hashmaps.

Watch Video