Sling Academy
Home/Rust/Rust: Iterating over key-value pairs in a HashMap with iter and iter_mut

Rust: Iterating over key-value pairs in a HashMap with iter and iter_mut

Last updated: January 04, 2025

In the Rust programming language, the HashMap from the standard library is widely used to store key-value pairs efficiently. However, when it comes to retrieving or modifying these values, you often need to iterate through the HashMap. Rust provides powerful iterators—iter and iter_mut—that facilitate these operations. Let's explore how to effectively use these iterators to navigate through a HashMap.

Understanding HashMap Basics

Before diving into iterators, here's a brief refresher on HashMap in Rust. A HashMap allows you to store data as a collection of key-value pairs. Both the keys and values are generic types, meaning you can use any data type that implements useful traits like PartialEq for keys and the value type.

use std::collections::HashMap;

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Blue", 10);
    scores.insert("Yellow", 50);
    scores.insert("Green", 30);
}

In this example, we created a HashMap named scores, associating team names to scores.

Using iter

The iter method borrows the entire HashMap in an immutable state. Through iter, we can safely read both keys and values of the HashMap without altering them.

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Blue", 10);
    scores.insert("Yellow", 50);
    scores.insert("Green", 30);

    for (team, &score) in scores.iter() {
        println!("{}: {}", team, score);
    }
}

In this code snippet, the loop iterates over the scores HashMap. Since iter borrows the HashMap immutably, we can't modify the scores, only read them.

Using iter_mut

If you need to modify the data within a HashMap while iterating, you will use iter_mut. This mutable iterator lets you borrow each value of the HashMap mutably, allowing mutations directly.

fn main() {
    let mut scores = HashMap::new();
    scores.insert("Blue", 10);
    scores.insert("Yellow", 50);
    scores.insert("Green", 30);

    for (_team, score) in scores.iter_mut() {
        *score += 10; // Increment each score by 10
    }

    for (team, score) in &scores {
        println!("{}: {}", team, score);
    }
}

In this example, each value in HashMap scores is incremented by 10. The mutable borrow, demonstrated with iter_mut, allows you to alter each score in the HashMap.

Comparing iter and iter_mut

In summary, the choice between iter and iter_mut depends on whether you need to modify the data:

  • Use iter when you are only interested in reading data from the HashMap. It provides a read-only reference to the values, which ensures your data remains unchanged.
  • Use iter_mut when you plan to make modifications to the HashMap's values. This grants mutable access, enabling you to update the data as necessary.

Using these methods adheres to Rust's principles of memory safety and concurrency by leveraging the borrowing system to assure safe access patterns. This way, you have the power of a concurrent, efficient data structure, combining the benefits of safe memory management without sacrificing performance.

So, next time you're handling a HashMap in Rust, rest easy knowing you can efficiently iterate through your data with either read-only or mutable access using iter and iter_mut.

Next Article: Rust - Transforming HashMap entries into other collections with collect

Previous Article: Rust: Using the entry API to handle default initialization in a HashMap

Series: Collections in Rust

Rust

You May Also Like

  • E0557 in Rust: Feature Has Been Removed or Is Unavailable in the Stable Channel
  • Network Protocol Handling Concurrency in Rust with async/await
  • Using the anyhow and thiserror Crates for Better Rust Error Tests
  • Rust - Investigating partial moves when pattern matching on vector or HashMap elements
  • Rust - Handling nested or hierarchical HashMaps for complex data relationships
  • Rust - Combining multiple HashMaps by merging keys and values
  • Composing Functionality in Rust Through Multiple Trait Bounds
  • E0437 in Rust: Unexpected `#` in macro invocation or attribute
  • Integrating I/O and Networking in Rust’s Async Concurrency
  • E0178 in Rust: Conflicting implementations of the same trait for a type
  • Utilizing a Reactor Pattern in Rust for Event-Driven Architectures
  • Parallelizing CPU-Intensive Work with Rust’s rayon Crate
  • Managing WebSocket Connections in Rust for Real-Time Apps
  • Downloading Files in Rust via HTTP for CLI Tools
  • Mocking Network Calls in Rust Tests with the surf or reqwest Crates
  • Rust - Designing advanced concurrency abstractions using generic channels or locks
  • Managing code expansion in debug builds with heavy usage of generics in Rust
  • Implementing parse-from-string logic for generic numeric types in Rust
  • Rust.- Refining trait bounds at implementation time for more specialized behavior